Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.

This commit is contained in:
LFeenanEA 2025-02-27 17:34:39 +00:00
parent 2e338c00cb
commit 3d0ee53a05
No known key found for this signature in database
GPG key ID: C6EBE8C2EA08F7E0
6072 changed files with 2283311 additions and 0 deletions

View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -0,0 +1,495 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// FILE: BaseType.h ///////////////////////////////////////////////////////////
//
// Project: RTS3
//
// Basic types and constants
// Author: Michael S. Booth, January 1995, September 2000
//
///////////////////////////////////////////////////////////////////////////////
// tell the compiler to only load this file once
#pragma once
#ifndef _BASE_TYPE_H_
#define _BASE_TYPE_H_
#include <math.h>
#include <string.h>
/*
** Turn off some unneeded warnings.
** Within the windows headers themselves, Microsoft has disabled the warnings 4290, 4514,
** 4069, 4200, 4237, 4103, 4001, 4035, 4164. Makes you wonder, eh?
*/
// "unreferenced inline function has been removed" Yea, so what?
#pragma warning(disable : 4514)
// Unreferenced local function removed.
#pragma warning(disable : 4505)
// 'unreferenced formal parameter'
#pragma warning(disable : 4100)
// 'identifier was truncated to '255' characters in the browser information':
// Tempates create LLLOOONNNGGG identifiers!
#pragma warning(disable : 4786)
// 'function selected for automatic inline expansion'. Cool, but since we're treating
// warnings as errors, don't warn me about this!
#pragma warning(disable : 4711)
#if 0
// 'assignment within condition expression'. actually a pretty useful warning,
// but way too much existing code violates it.
//#pragma warning(disable : 4706)
#else
// actually, it turned out not to be too bad, so this is now ENABLED. (srj)
#pragma warning(error : 4706)
#endif
// 'conditional expression is constant'. used lots in debug builds.
#pragma warning(disable : 4127)
// 'nonstandard extension used : nameless struct/union'. MS headers violate this...
#pragma warning(disable : 4201)
// 'unreachable code'. STL violates this...
#pragma warning(disable : 4702)
// 'local variable is initialized but not referenced'. good thing to know about...
#pragma warning(error : 4189)
// 'unreferenced local variable'. good thing to know about...
#pragma warning(error : 4101)
#ifndef PI
#define PI 3.14159265359f
#define TWO_PI 6.28318530718f
#endif
#ifndef NULL
//#define NULL ((void *)0)
#define NULL 0 // C++ doesn't like casting void *'s into other pointers
#endif
// MSVC math.h defines overloaded functions with this name...
//#ifndef abs
//#define abs(x) (((x) < 0) ? -(x) : (x))
//#endif
#ifndef min
#define min(x,y) (((x)<(y)) ? (x) : (y))
#endif
#ifndef max
#define max(x,y) (((x)>(y)) ? (x) : (y))
#endif
#ifndef TRUE
#define TRUE true
#endif
#ifndef FALSE
#define FALSE false
#endif
// Elements in an array
#ifndef ELEMENTS_OF
#define ELEMENTS_OF( x ) ( sizeof( x ) / sizeof( x[0] ) )
#endif
//--------------------------------------------------------------------
// Fundamental type definitions
//--------------------------------------------------------------------
typedef float Real; // 4 bytes
typedef int Int; // 4 bytes
typedef unsigned int UnsignedInt; // 4 bytes
typedef unsigned short UnsignedShort; // 2 bytes
typedef short Short; // 2 bytes
typedef unsigned char UnsignedByte; // 1 byte USED TO BE "Byte"
typedef char Byte; // 1 byte USED TO BE "SignedByte"
typedef char Char; // 1 byte of text
typedef bool Bool; //
// note, the types below should use "long long", but MSVC doesn't support it yet
typedef __int64 Int64; // 8 bytes
typedef unsigned __int64 UnsignedInt64; // 8 bytes
#include "Lib/Trig.h"
//-----------------------------------------------------------------------------
typedef wchar_t WideChar; ///< multi-byte character representations
//-----------------------------------------------------------------------------
template <typename NUM>
inline NUM sqr(NUM x)
{
return x*x;
}
template <typename NUM>
inline NUM clamp(NUM lo, NUM val, NUM hi)
{
if (val < lo) return lo;
else if (val > hi) return hi;
else return val;
}
template <typename NUM>
inline int sign(NUM x)
{
if (x > 0) return 1;
else if (x < 0) return -1;
else return 0;
}
//-----------------------------------------------------------------------------
inline Real rad2deg(Real rad) { return rad * (180/PI); }
inline Real deg2rad(Real rad) { return rad * (PI/180); }
//-----------------------------------------------------------------------------
// For twiddling bits
//-----------------------------------------------------------------------------
#define BitTest( x, i ) ( ( (x) & (i) ) != 0 )
#define BitSet( x, i ) ( (x) |= (i) )
#define BitClear( x, i ) ( (x ) &= ~(i) )
#define BitToggle( x, i ) ( (x) ^= (i) )
//-------------------------------------------------------------------------------------------------
// note, this function depends on the cpu rounding mode, which we set to CHOP every frame,
// but apparently tends to be left in unpredictable modes by various system bits of
// code, so use this function with caution -- it might not round in the way you want.
__forceinline long fast_float2long_round(float f)
{
long i;
__asm {
fld [f]
fistp [i]
}
return i;
}
// super fast float trunc routine, works always (independent of any FPU modes)
// code courtesy of Martin Hoffesommer (grin)
__forceinline float fast_float_trunc(float f)
{
_asm
{
mov ecx,[f]
shr ecx,23
mov eax,0xff800000
xor ebx,ebx
sub cl,127
cmovc eax,ebx
sar eax,cl
and [f],eax
}
return f;
}
// same here, fast floor function
__forceinline float fast_float_floor(float f)
{
static unsigned almost1=(126<<23)|0x7fffff;
if (*(unsigned *)&f &0x80000000)
f-=*(float *)&almost1;
return fast_float_trunc(f);
}
// same here, fast ceil function
__forceinline float fast_float_ceil(float f)
{
static unsigned almost1=(126<<23)|0x7fffff;
if ( (*(unsigned *)&f &0x80000000)==0)
f+=*(float *)&almost1;
return fast_float_trunc(f);
}
//-------------------------------------------------------------------------------------------------
#define REAL_TO_INT(x) ((Int)(fast_float2long_round(fast_float_trunc(x))))
#define REAL_TO_UNSIGNEDINT(x) ((UnsignedInt)(fast_float2long_round(fast_float_trunc(x))))
#define REAL_TO_SHORT(x) ((Short)(fast_float2long_round(fast_float_trunc(x))))
#define REAL_TO_UNSIGNEDSHORT(x) ((UnsignedShort)(fast_float2long_round(fast_float_trunc(x))))
#define REAL_TO_BYTE(x) ((Byte)(fast_float2long_round(fast_float_trunc(x))))
#define REAL_TO_UNSIGNEDBYTE(x) ((UnsignedByte)(fast_float2long_round(fast_float_trunc(x))))
#define REAL_TO_CHAR(x) ((Char)(fast_float2long_round(fast_float_trunc(x))))
#define DOUBLE_TO_REAL(x) ((Real) (x))
#define DOUBLE_TO_INT(x) ((Int) (fast_float2long_round(fast_float_trunc(x))))
#define INT_TO_REAL(x) ((Real) (x))
// once we've ceiled/floored, trunc and round are identical, and currently, round is faster... (srj)
#define REAL_TO_INT_CEIL(x) (fast_float2long_round(fast_float_ceil(x)))
#define REAL_TO_INT_FLOOR(x) (fast_float2long_round(fast_float_floor(x)))
#define FAST_REAL_TRUNC(x) fast_float_trunc(x)
#define FAST_REAL_CEIL(x) fast_float_ceil(x)
#define FAST_REAL_FLOOR(x) fast_float_floor(x)
//--------------------------------------------------------------------
// Derived type definitions
//--------------------------------------------------------------------
// NOTE: Keep these derived types simple, and avoid constructors and destructors
// so they can be used within unions.
// real-valued range defined by low and high values
struct RealRange
{
Real lo, hi; // low and high values of the range
// combine the given range with us such that we now encompass
// both ranges
void combine( RealRange &other )
{
lo = min( lo, other.lo );
hi = max( hi, other.hi );
}
};
struct Coord2D
{
Real x, y;
Real length( void ) const { return (Real)sqrt( x*x + y*y ); }
void normalize( void )
{
Real len = length();
if( len != 0 )
{
x /= len;
y /= len;
}
}
Real toAngle( void ) const; ///< turn 2D vector into angle (where angle 0 is down the +x axis)
};
inline Real Coord2D::toAngle( void ) const
{
const Real len = length();
if (len == 0.0f)
return 0.0f;
Real c = x/len;
// bound it in case of numerical error
if (c < -1.0f)
c = -1.0f;
else if (c > 1.0f)
c = 1.0f;
return y < 0.0f ? -ACos(c) : ACos(c);
} // end toAngle
struct ICoord2D
{
Int x, y;
Int length( void ) const { return (Int)sqrt( (double)(x*x + y*y) ); }
};
struct Region2D
{
Coord2D lo, hi; // bounds of 2D rectangular region
Real width( void ) const { return hi.x - lo.x; }
Real height( void ) const { return hi.y - lo.y; }
};
struct IRegion2D
{
ICoord2D lo, hi; // bounds of 2D rectangular region
Int width( void ) const { return hi.x - lo.x; }
Int height( void ) const { return hi.y - lo.y; }
};
struct Coord3D
{
Real x, y, z;
Real length( void ) const { return (Real)sqrt( x*x + y*y + z*z ); }
Real lengthSqr( void ) const { return ( x*x + y*y + z*z ); }
void normalize( void )
{
Real len = length();
if( len != 0 )
{
x /= len;
y /= len;
z /= len;
}
}
static void crossProduct( const Coord3D *a, const Coord3D *b, Coord3D *r )
{
r->x = (a->y * b->z - a->z * b->y);
r->y = (a->z * b->x - a->x * b->z);
r->z = (a->x * b->y - a->y * b->x);
}
void zero( void )
{
x = 0.0f;
y = 0.0f;
z = 0.0f;
}
void add( const Coord3D *a )
{
x += a->x;
y += a->y;
z += a->z;
}
void sub( const Coord3D *a )
{
x -= a->x;
y -= a->y;
z -= a->z;
}
void set( const Coord3D *a )
{
x = a->x;
y = a->y;
z = a->z;
}
void set( Real ax, Real ay, Real az )
{
x = ax;
y = ay;
z = az;
}
void scale( Real scale )
{
x *= scale;
y *= scale;
z *= scale;
}
Bool equals( const Coord3D &r )
{
return (x == r.x &&
y == r.y &&
z == r.z);
}
Bool operator==( const Coord3D &r )
{
return (x == r.x &&
y == r.y &&
z == r.z);
}
};
struct ICoord3D
{
Int x, y, z;
Int length( void ) const { return (Int)sqrt( (double)(x*x + y*y + z*z) ); }
void zero( void )
{
x = 0;
y = 0;
z = 0;
}
};
struct Region3D
{
Coord3D lo, hi; // axis-aligned bounding box
Real width( void ) const { return hi.x - lo.x; }
Real height( void ) const { return hi.y - lo.y; }
Real depth( void ) const { return hi.z - lo.z; }
void zero() { lo.zero(); hi.zero(); }
Bool isInRegionNoZ( const Coord3D *query ) const
{
return (lo.x < query->x) && (query->x < hi.x)
&& (lo.y < query->y) && (query->y < hi.y);
}
Bool isInRegionWithZ( const Coord3D *query ) const
{
return (lo.x < query->x) && (query->x < hi.x)
&& (lo.y < query->y) && (query->y < hi.y)
&& (lo.z < query->z) && (query->z < hi.z);
}
};
struct IRegion3D
{
ICoord3D lo, hi; // axis-aligned bounding box
Int width( void ) const { return hi.x - lo.x; }
Int height( void ) const { return hi.y - lo.y; }
Int depth( void ) const { return hi.z - lo.z; }
};
struct RGBColor
{
Real red, green, blue; // range between 0 and 1
inline Int getAsInt() const
{
return
((Int)(red * 255.0) << 16) |
((Int)(green * 255.0) << 8) |
((Int)(blue * 255.0) << 0);
}
inline void setFromInt(Int c)
{
red = ((c >> 16) & 0xff) / 255.0f;
green = ((c >> 8) & 0xff) / 255.0f;
blue = ((c >> 0) & 0xff) / 255.0f;
}
};
struct RGBAColorReal
{
Real red, green, blue, alpha; // range between 0.0 and 1.0
};
struct RGBAColorInt
{
UnsignedInt red, green, blue, alpha; // range between 0 and 255
};
#endif // _BASE_TYPE_H_

View file

@ -0,0 +1,33 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Trig.h
// fast trig functions
// Author: Sondra Iverson, March 1998
// Converted to Generals by Matthew D. Campbell, February 2002
#ifndef _TRIG_H_
#define _TRIG_H_
Real Sin(Real);
Real Cos(Real);
Real Tan(Real);
Real ACos(Real);
Real ASin(Real x);
#endif // _TRIG_H_

View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -0,0 +1,37 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/////////////////////////////////////////////////////////////////////////EA-V1
// $File: //depot/GeneralsMD/Staging/code/Libraries/Include/rts/debug.h $
// $Author: mhoffe $
// $Revision: #1 $
// $DateTime: 2003/07/03 11:55:26 $
//
// ©2003 Electronic Arts
//
// Proxy header for debug module
//////////////////////////////////////////////////////////////////////////////
#ifdef _MSC_VER
# pragma once
#endif
#ifndef PROXY_DEBUG_H // Include guard
#define PROXY_DEBUG_H
#include "../../source/debug/debug.h"
#endif // PROXY_DEBUG_H

View file

@ -0,0 +1,37 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/////////////////////////////////////////////////////////////////////////EA-V1
// $File: //depot/GeneralsMD/Staging/code/Libraries/Include/rts/profile.h $
// $Author: mhoffe $
// $Revision: #1 $
// $DateTime: 2003/07/03 11:55:26 $
//
// ©2003 Electronic Arts
//
// Proxy header for profile module
//////////////////////////////////////////////////////////////////////////////
#ifdef _MSC_VER
# pragma once
#endif
#ifndef PROXY_PROFILE_H // Include guard
#define PROXY_PROFILE_H
#include "../../source/profile/profile.h"
#endif // PROXY_PROFILE_H

View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -0,0 +1,144 @@
# Microsoft Developer Studio Project File - Name="Benchmark" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Static Library" 0x0104
CFG=Benchmark - 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 "Benchmark.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 "Benchmark.mak" CFG="Benchmark - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "Benchmark - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "Benchmark - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "Benchmark"
# PROP Scc_LocalPath "."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "Benchmark - 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 Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"..\..\Lib\Benchmark.lib"
!ELSEIF "$(CFG)" == "Benchmark - 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 Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"..\..\Lib\BenchmarkD.lib"
!ENDIF
# Begin Target
# Name "Benchmark - Win32 Release"
# Name "Benchmark - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\emfloat.c
# End Source File
# Begin Source File
SOURCE=.\misc.c
# End Source File
# Begin Source File
SOURCE=.\nbench0.c
# End Source File
# Begin Source File
SOURCE=.\nbench1.c
# End Source File
# Begin Source File
SOURCE=.\sysspec.c
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\benchmark.h
# End Source File
# Begin Source File
SOURCE=.\emfloat.h
# End Source File
# Begin Source File
SOURCE=.\misc.h
# End Source File
# Begin Source File
SOURCE=.\nbench0.h
# End Source File
# Begin Source File
SOURCE=.\nbench1.h
# End Source File
# Begin Source File
SOURCE=.\nmglobal.h
# End Source File
# Begin Source File
SOURCE=.\sysspec.h
# End Source File
# Begin Source File
SOURCE=.\wordcat.h
# End Source File
# End Group
# End Target
# End Project

View file

@ -0,0 +1,34 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __BENCHMARK_H__
#define __BENCHMARK_H__
#ifdef __cplusplus
extern "C" {
#endif
int RunBenchmark(int argc, char *argv[], float *floatResult, float *intResult, float *memResult);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,369 @@
# Microsoft Developer Studio Project File - Name="Compression" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Static Library" 0x0104
CFG=Compression - 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 "Compression.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 "Compression.mak" CFG="Compression - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "Compression - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "Compression - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE "Compression - Win32 Internal" (based on "Win32 (x86) Static Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "Compression"
# PROP Scc_LocalPath "..\.."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "Compression - 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 Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /W3 /WX /GX /O2 /Ob2 /I "..\..\include" /D "NDEBUG" /D "_RELEASE" /D "WIN32" /D "_MBCS" /D "_LIB" /D "Z_PREFIX" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
!ELSEIF "$(CFG)" == "Compression - 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 Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /WX /Gm /GX /ZI /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "Z_PREFIX" /FR /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
!ELSEIF "$(CFG)" == "Compression - Win32 Internal"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Internal"
# PROP BASE Intermediate_Dir "Internal"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Internal"
# PROP Intermediate_Dir "Internal"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /W3 /WX /GX /O2 /I "..\..\include" /D "NDEBUG" /D "_INTERNAL" /D "WIN32" /D "_MBCS" /D "_LIB" /D "Z_PREFIX" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
!ENDIF
# Begin Target
# Name "Compression - Win32 Release"
# Name "Compression - Win32 Debug"
# Name "Compression - Win32 Internal"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Group "Zlib"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\ZLib\adler32.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\compress.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\crc32.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\deflate.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\gzio.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\infblock.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\infcodes.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\inffast.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\inflate.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\inftrees.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\infutil.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\maketree.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\trees.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\uncompr.c
# End Source File
# Begin Source File
SOURCE=.\ZLib\zutil.c
# End Source File
# End Group
# Begin Group "NoxLZH"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\LZHCompress\CompLibSource\Hdec_g.tbl
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\CompLibSource\Hdec_s.tbl
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\CompLibSource\Hdisp.tbl
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\CompLibSource\Henc.tbl
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\CompLibSource\Huff.cpp
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\CompLibSource\Lz.cpp
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\CompLibSource\Lzhl.cpp
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\CompLibSource\Lzhl_tcp.cpp
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\NoxCompress.cpp
# End Source File
# End Group
# Begin Group "EAC"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\EAC\btreeabout.cpp
# End Source File
# Begin Source File
SOURCE=.\EAC\btreedecode.cpp
# End Source File
# Begin Source File
SOURCE=.\EAC\btreeencode.cpp
# End Source File
# Begin Source File
SOURCE=.\EAC\huffabout.cpp
# End Source File
# Begin Source File
SOURCE=.\EAC\huffdecode.cpp
# End Source File
# Begin Source File
SOURCE=.\EAC\huffencode.cpp
# End Source File
# Begin Source File
SOURCE=.\EAC\refabout.cpp
# End Source File
# Begin Source File
SOURCE=.\EAC\refdecode.cpp
# End Source File
# Begin Source File
SOURCE=.\EAC\refencode.cpp
# End Source File
# End Group
# Begin Source File
SOURCE=.\CompressionManager.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Group "Zlib.H"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\ZLib\deflate.h
# End Source File
# Begin Source File
SOURCE=.\ZLib\infblock.h
# End Source File
# Begin Source File
SOURCE=.\ZLib\infcodes.h
# End Source File
# Begin Source File
SOURCE=.\ZLib\inffast.h
# End Source File
# Begin Source File
SOURCE=.\ZLib\inffixed.h
# End Source File
# Begin Source File
SOURCE=.\ZLib\inftrees.h
# End Source File
# Begin Source File
SOURCE=.\ZLib\infutil.h
# End Source File
# Begin Source File
SOURCE=.\ZLib\trees.h
# End Source File
# Begin Source File
SOURCE=.\ZLib\zconf.h
# End Source File
# Begin Source File
SOURCE=.\ZLib\zlib.h
# End Source File
# Begin Source File
SOURCE=.\ZLib\zutil.h
# End Source File
# End Group
# Begin Group "NoxLZH.H"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\LZHCompress\CompLibHeader\_huff.h
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\CompLibHeader\_lz.h
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\CompLibHeader\_lzhl.h
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\CompLibHeader\Lzhl.h
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\CompLibHeader\Lzhl_tcp.h
# End Source File
# Begin Source File
SOURCE=.\LZHCompress\NoxCompress.h
# End Source File
# End Group
# Begin Group "EAC.H"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\EAC\btreecodex.h
# End Source File
# Begin Source File
SOURCE=.\EAC\codex.h
# End Source File
# Begin Source File
SOURCE=.\EAC\gimex.h
# End Source File
# Begin Source File
SOURCE=.\EAC\huffcodex.h
# End Source File
# Begin Source File
SOURCE=.\EAC\refcodex.h
# End Source File
# End Group
# Begin Source File
SOURCE=.\Compression.h
# End Source File
# End Group
# End Target
# End Project

View file

@ -0,0 +1,71 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// FILE: Compression.h ///////////////////////////////////////////////////////
// Author: Matthew D. Campbell
//////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef __COMPRESSION_H__
#define __COMPRESSION_H__
#include "Lib/BaseType.h"
enum CompressionType
{
COMPRESSION_MIN = 0,
COMPRESSION_NONE = COMPRESSION_MIN,
COMPRESSION_REFPACK,
COMPRESSION_MAX = COMPRESSION_REFPACK,
COMPRESSION_NOXLZH,
COMPRESSION_ZLIB1,
COMPRESSION_ZLIB2,
COMPRESSION_ZLIB3,
COMPRESSION_ZLIB4,
COMPRESSION_ZLIB5,
COMPRESSION_ZLIB6,
COMPRESSION_ZLIB7,
COMPRESSION_ZLIB8,
COMPRESSION_ZLIB9,
COMPRESSION_BTREE,
COMPRESSION_HUFF,
};
class CompressionManager
{
public:
static Bool isDataCompressed( const void *mem, Int len );
static CompressionType getCompressionType( const void *mem, Int len );
static Int getMaxCompressedSize( Int uncompressedLen, CompressionType compType );
static Int getUncompressedSize( const void *mem, Int len );
static Int compressData( CompressionType compType, void *src, Int srcLen, void *dest, Int destLen ); // 0 on error
static Int decompressData( void *src, Int srcLen, void *dest, Int destLen ); // 0 on error
static const char *getCompressionNameByType( CompressionType compType );
// For perf timers, so we can have separate ones for compression/decompression
static const char *getDecompressionNameByType( CompressionType compType );
static CompressionType getPreferredCompression( void );
};
#endif // __COMPRESSION_H__

View file

@ -0,0 +1,509 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// FILE: Compression.cpp /////////////////////////////////////////////////////
// Author: Matthew D. Campbell
// LZH wrapper taken from Nox, originally from Jeff Brown
//////////////////////////////////////////////////////////////////////////////
#include "Compression.h"
#include "LZHCompress/NoxCompress.h"
extern "C" {
#include "ZLib/zlib.h"
}
#include "EAC/codex.h"
#include "EAC/btreecodex.h"
#include "EAC/huffcodex.h"
#include "EAC/refcodex.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma message("************************************** WARNING, optimization disabled for debugging purposes")
#endif
#define DEBUG_LOG(x) {}
const char *CompressionManager::getCompressionNameByType( CompressionType compType )
{
static const char *s_compressionNames[COMPRESSION_MAX+1] = {
"No compression",
"RefPack",
/*
"LZHL",
"ZLib 1 (fast)",
"ZLib 2",
"ZLib 3",
"ZLib 4",
"ZLib 5 (default)",
"ZLib 6",
"ZLib 7",
"ZLib 8",
"ZLib 9 (slow)",
"BTree",
"Huff",
*/
};
return s_compressionNames[compType];
}
// For perf timers, so we can have separate ones for compression/decompression
const char *CompressionManager::getDecompressionNameByType( CompressionType compType )
{
static const char *s_decompressionNames[COMPRESSION_MAX+1] = {
"d_None",
"d_RefPack",
/*
"d_NoxLZW",
"d_ZLib1",
"d_ZLib2",
"d_ZLib3",
"d_ZLib4",
"d_ZLib5",
"d_ZLib6",
"d_ZLib7",
"d_ZLib8",
"d_ZLib9",
"d_BTree",
"d_Huff",
*/
};
return s_decompressionNames[compType];
}
// ---------------------------------------------------------------------------------------
Bool CompressionManager::isDataCompressed( const void *mem, Int len )
{
CompressionType t = getCompressionType(mem, len);
return t != COMPRESSION_NONE;
}
CompressionType CompressionManager::getPreferredCompression( void )
{
return COMPRESSION_REFPACK;
}
CompressionType CompressionManager::getCompressionType( const void *mem, Int len )
{
if (len < 8)
return COMPRESSION_NONE;
if ( memcmp( mem, "NOX\0", 4 ) == 0 )
return COMPRESSION_NOXLZH;
if ( memcmp( mem, "ZL1\0", 4 ) == 0 )
return COMPRESSION_ZLIB1;
if ( memcmp( mem, "ZL2\0", 4 ) == 0 )
return COMPRESSION_ZLIB2;
if ( memcmp( mem, "ZL3\0", 4 ) == 0 )
return COMPRESSION_ZLIB3;
if ( memcmp( mem, "ZL4\0", 4 ) == 0 )
return COMPRESSION_ZLIB4;
if ( memcmp( mem, "ZL5\0", 4 ) == 0 )
return COMPRESSION_ZLIB5;
if ( memcmp( mem, "ZL6\0", 4 ) == 0 )
return COMPRESSION_ZLIB6;
if ( memcmp( mem, "ZL7\0", 4 ) == 0 )
return COMPRESSION_ZLIB7;
if ( memcmp( mem, "ZL8\0", 4 ) == 0 )
return COMPRESSION_ZLIB8;
if ( memcmp( mem, "ZL9\0", 4 ) == 0 )
return COMPRESSION_ZLIB9;
if ( memcmp( mem, "EAB\0", 4 ) == 0 )
return COMPRESSION_BTREE;
if ( memcmp( mem, "EAH\0", 4 ) == 0 )
return COMPRESSION_HUFF;
if ( memcmp( mem, "EAR\0", 4 ) == 0 )
return COMPRESSION_REFPACK;
return COMPRESSION_NONE;
}
Int CompressionManager::getMaxCompressedSize( Int uncompressedLen, CompressionType compType )
{
switch (compType)
{
case COMPRESSION_NOXLZH:
return CalcNewSize(uncompressedLen) + 8;
case COMPRESSION_BTREE: // guessing here
case COMPRESSION_HUFF: // guessing here
case COMPRESSION_REFPACK: // guessing here
return uncompressedLen + 8;
case COMPRESSION_ZLIB1:
case COMPRESSION_ZLIB2:
case COMPRESSION_ZLIB3:
case COMPRESSION_ZLIB4:
case COMPRESSION_ZLIB5:
case COMPRESSION_ZLIB6:
case COMPRESSION_ZLIB7:
case COMPRESSION_ZLIB8:
case COMPRESSION_ZLIB9:
return (Int)(ceil(uncompressedLen * 1.1 + 12 + 8));
}
return 0;
}
Int CompressionManager::getUncompressedSize( const void *mem, Int len )
{
if (len < 8)
return len;
CompressionType compType = getCompressionType( mem, len );
switch (compType)
{
case COMPRESSION_NOXLZH:
case COMPRESSION_ZLIB1:
case COMPRESSION_ZLIB2:
case COMPRESSION_ZLIB3:
case COMPRESSION_ZLIB4:
case COMPRESSION_ZLIB5:
case COMPRESSION_ZLIB6:
case COMPRESSION_ZLIB7:
case COMPRESSION_ZLIB8:
case COMPRESSION_ZLIB9:
case COMPRESSION_BTREE:
case COMPRESSION_HUFF:
case COMPRESSION_REFPACK:
return *(Int *)(((UnsignedByte *)mem)+4);
}
return len;
}
Int CompressionManager::compressData( CompressionType compType, void *srcVoid, Int srcLen, void *destVoid, Int destLen )
{
if (destLen < 8)
return 0;
destLen -= 8;
UnsignedByte *src = (UnsignedByte *)srcVoid;
UnsignedByte *dest = (UnsignedByte *)destVoid;
if (compType == COMPRESSION_BTREE)
{
memcpy(dest, "EAB\0", 4);
*(Int *)(dest+4) = 0;
Int ret = BTREE_encode(dest+8, src, srcLen);
if (ret)
{
*(Int *)(dest+4) = srcLen;
return ret + 8;
}
else
return 0;
}
if (compType == COMPRESSION_HUFF)
{
memcpy(dest, "EAH\0", 4);
*(Int *)(dest+4) = 0;
Int ret = HUFF_encode(dest+8, src, srcLen);
if (ret)
{
*(Int *)(dest+4) = srcLen;
return ret + 8;
}
else
return 0;
}
if (compType == COMPRESSION_REFPACK)
{
memcpy(dest, "EAR\0", 4);
*(Int *)(dest+4) = 0;
Int ret = REF_encode(dest+8, src, srcLen);
if (ret)
{
*(Int *)(dest+4) = srcLen;
return ret + 8;
}
else
return 0;
}
if (compType == COMPRESSION_NOXLZH)
{
memcpy(dest, "NOX\0", 4);
*(Int *)(dest+4) = 0;
Bool ret = CompressMemory(src, srcLen, dest+8, destLen);
if (ret)
{
*(Int *)(dest+4) = srcLen;
return destLen + 8;
}
else
return 0;
}
if (compType >= COMPRESSION_ZLIB1 && compType <= COMPRESSION_ZLIB9)
{
Int level = compType - COMPRESSION_ZLIB1 + 1; // 1-9
memcpy(dest, "ZL0\0", 4);
dest[2] = '0' + level;
*(Int *)(dest+4) = 0;
unsigned long outLen = destLen;
Int err = z_compress2( dest+8, &outLen, src, srcLen, level );
if (err == Z_OK || err == Z_STREAM_END)
{
*(Int *)(dest+4) = srcLen;
return outLen + 8;
}
else
{
DEBUG_LOG(("ZLib compression error (level is %d, src len is %d) %d\n", level, srcLen, err));
return 0;
}
}
return 0;
}
Int CompressionManager::decompressData( void *srcVoid, Int srcLen, void *destVoid, Int destLen )
{
if (srcLen < 8)
return 0;
UnsignedByte *src = (UnsignedByte *)srcVoid;
UnsignedByte *dest = (UnsignedByte *)destVoid;
CompressionType compType = getCompressionType(src, srcLen);
if (compType == COMPRESSION_BTREE)
{
Int slen = srcLen - 8;
Int ret = BTREE_decode(dest, src+8, &slen);
if (ret)
return ret;
else
return 0;
}
if (compType == COMPRESSION_HUFF)
{
Int slen = srcLen - 8;
Int ret = HUFF_decode(dest, src+8, &slen);
if (ret)
return ret;
else
return 0;
}
if (compType == COMPRESSION_REFPACK)
{
Int slen = srcLen - 8;
Int ret = REF_decode(dest, src+8, &slen);
if (ret)
return ret;
else
return 0;
}
if (compType == COMPRESSION_NOXLZH)
{
Bool ret = DecompressMemory(src+8, srcLen-8, dest, destLen);
if (ret)
return destLen;
else
return 0;
}
if (compType >= COMPRESSION_ZLIB1 && compType <= COMPRESSION_ZLIB9)
{
#ifdef DEBUG_LOGGING
Int level = compType - COMPRESSION_ZLIB1 + 1; // 1-9
#endif
unsigned long outLen = destLen;
Int err = z_uncompress(dest, &outLen, src+8, srcLen-8);
if (err == Z_OK || err == Z_STREAM_END)
{
return outLen;
}
else
{
DEBUG_LOG(("ZLib decompression error (src is level %d, %d bytes long) %d\n", level, srcLen, err));
return 0;
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///// Performance Testing ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
#ifdef TEST_COMPRESSION
#include "GameClient/MapUtil.h"
#include "Common/FileSystem.h"
#include "Common/File.h"
#include "Common/PerfTimer.h"
enum { NUM_TIMES = 10 };
struct CompData
{
public:
Int origSize;
Int compressedSize[COMPRESSION_MAX+1];
};
void DoCompressTest( void )
{
Int i;
PerfGather *s_compressGathers[COMPRESSION_MAX+1];
PerfGather *s_decompressGathers[COMPRESSION_MAX+1];
for (i = 0; i < COMPRESSION_MAX+1; ++i)
{
s_compressGathers[i] = new PerfGather(CompressionManager::getCompressionNameByType((CompressionType)i));
s_decompressGathers[i] = new PerfGather(CompressionManager::getDecompressionNameByType((CompressionType)i));
}
std::map<AsciiString, CompData> s_sizes;
std::map<AsciiString, MapMetaData>::const_iterator it = TheMapCache->begin();
while (it != TheMapCache->end())
{
//if (it->second.m_isOfficial)
//{
//++it;
//continue;
//}
//static Int count = 0;
//if (count++ > 2)
//break;
File *f = TheFileSystem->openFile(it->first.str());
if (f)
{
DEBUG_LOG(("***************************\nTesting '%s'\n\n", it->first.str()));
Int origSize = f->size();
UnsignedByte *buf = (UnsignedByte *)f->readEntireAndClose();
UnsignedByte *uncompressedBuf = NEW UnsignedByte[origSize];
CompData d = s_sizes[it->first];
d.origSize = origSize;
d.compressedSize[COMPRESSION_NONE] = origSize;
for (i=COMPRESSION_MIN; i<=COMPRESSION_MAX; ++i)
{
DEBUG_LOG(("=================================================\n"));
DEBUG_LOG(("Compression Test %d\n", i));
Int maxCompressedSize = CompressionManager::getMaxCompressedSize( origSize, (CompressionType)i );
DEBUG_LOG(("Orig size is %d, max compressed size is %d bytes\n", origSize, maxCompressedSize));
UnsignedByte *compressedBuf = NEW UnsignedByte[maxCompressedSize];
memset(compressedBuf, 0, maxCompressedSize);
memset(uncompressedBuf, 0, origSize);
Int compressedLen, decompressedLen;
for (Int j=0; j < NUM_TIMES; ++j)
{
s_compressGathers[i]->startTimer();
compressedLen = CompressionManager::compressData((CompressionType)i, buf, origSize, compressedBuf, maxCompressedSize);
s_compressGathers[i]->stopTimer();
s_decompressGathers[i]->startTimer();
decompressedLen = CompressionManager::decompressData(compressedBuf, compressedLen, uncompressedBuf, origSize);
s_decompressGathers[i]->stopTimer();
}
d.compressedSize[i] = compressedLen;
DEBUG_LOG(("Compressed len is %d (%g%% of original size)\n", compressedLen, (double)compressedLen/(double)origSize*100.0));
DEBUG_ASSERTCRASH(compressedLen, ("Failed to compress\n"));
DEBUG_LOG(("Decompressed len is %d (%g%% of original size)\n", decompressedLen, (double)decompressedLen/(double)origSize*100.0));
DEBUG_ASSERTCRASH(decompressedLen == origSize, ("orig size does not match compressed+uncompressed output\n"));
if (decompressedLen == origSize)
{
Int ret = memcmp(buf, uncompressedBuf, origSize);
if (ret != 0)
{
DEBUG_CRASH(("orig buffer does not match compressed+uncompressed output - ret was %d\n", ret));
}
}
delete compressedBuf;
compressedBuf = NULL;
}
DEBUG_LOG(("d = %d -> %d\n", d.origSize, d.compressedSize[i]));
s_sizes[it->first] = d;
DEBUG_LOG(("s_sizes[%s] = %d -> %d\n", it->first.str(), s_sizes[it->first].origSize, s_sizes[it->first].compressedSize[i]));
delete[] buf;
buf = NULL;
delete[] uncompressedBuf;
uncompressedBuf = NULL;
}
++it;
}
for (i=COMPRESSION_MIN; i<=COMPRESSION_MAX; ++i)
{
Real maxCompression = 1000.0f;
Real minCompression = 0.0f;
Int totalUncompressedBytes = 0;
Int totalCompressedBytes = 0;
for (std::map<AsciiString, CompData>::iterator cd = s_sizes.begin(); cd != s_sizes.end(); ++cd)
{
CompData d = cd->second;
Real ratio = d.compressedSize[i]/(Real)d.origSize;
maxCompression = min(maxCompression, ratio);
minCompression = max(minCompression, ratio);
totalUncompressedBytes += d.origSize;
totalCompressedBytes += d.compressedSize[i];
}
DEBUG_LOG(("***************************************************\n"));
DEBUG_LOG(("Compression method %s:\n", CompressionManager::getCompressionNameByType((CompressionType)i)));
DEBUG_LOG(("%d bytes compressed to %d (%g%%)\n", totalUncompressedBytes, totalCompressedBytes,
totalCompressedBytes/(Real)totalUncompressedBytes*100.0f));
DEBUG_LOG(("Min ratio: %g%%, Max ratio: %g%%\n",
minCompression*100.0f, maxCompression*100.0f));
DEBUG_LOG(("\n"));
}
PerfGather::dumpAll(10000);
//PerfGather::displayGraph(TheGameLogic->getFrame());
PerfGather::resetAll();
CopyFile( "AAAPerfStats.csv", "AAACompressPerfStats.csv", FALSE );
for (i = 0; i < COMPRESSION_MAX+1; ++i)
{
delete s_compressGathers[i];
s_compressGathers[i] = NULL;
delete s_decompressGathers[i];
s_decompressGathers[i] = NULL;
}
}
#endif // TEST_COMPRESSION

View file

@ -0,0 +1,105 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Copyright (C) Electronic Arts Canada Inc. 1995-2002. All rights reserved.
/* ABSTRACT */
/*------------------------------------------------------------------*/
/* */
/* BTree - Binary Tree Codex */
/* */
/* by FrANK G. Barchard, EAC */
/* */
/*------------------------------------------------------------------*/
/* */
/* Version Date SE History: */
/* ------- ---- -- -------- */
/* 1.00 950108 FB BTree codex based on hufftree and ref codex */
/* 1.01 970117 FB encode check index before going off array */
/* 1.02 020716 FB allocate percentage more buffer for large files*/
/* */
/*------------------------------------------------------------------*/
/* */
/* Module Notes: */
/* ------------- */
/* Reentrant */
/* Files: btrread.c btrwrite.c btrcodex.h */
/* */
/*------------------------------------------------------------------*/
/* */
/* Format Notes: */
/* ------------- */
/* BTree is an EA proprietary compression scheme by Frank Barchard. */
/* Each byte is either a raw byte (leaf) or node that points to */
/* 2 other nodes. Each node is either a simple byte or 2 nodes. */
/* The stream is simple bytes and uses bytes for nodes that werent */
/* used in the original file. */
/* */
/* BTREE (fb6) header format: */
/* -------------------------- */
/* */
/* offset bytes notes */
/* id 0 2 id is 46fb */
/* ulen 2 3 total unpacked len */
/* ilen* 2/5 3 unpacked len for this file */
/* clue 5/8 1 */
/* nodes 6/9 1 number of nodes */
/* { */
/* node 7/10+3n 1 */
/* left 8/11+3n 1 */
/* right 9/12+3n 1 */
/* } */
/* */
/* [packed data] */
/* [explicitely packed last byte] */
/* */
/* * present if composite packed */
/* */
/*------------------------------------------------------------------*/
/* END ABSTRACT */
#include <string.h>
#include "codex.h"
#include "btreecodex.h"
/****************************************************************/
/* Information Functions */
/****************************************************************/
CODEXABOUT *GCALL BTREE_about(void)
{
CODEXABOUT* info;
info = (CODEXABOUT*) galloc(sizeof(CODEXABOUT));
if (info)
{
memset(info, 0, sizeof(CODEXABOUT));
info->signature = QMAKEID('B','T','R','E');
info->size = sizeof(CODEXABOUT);
info->version = 200; /* codex version number (200) */
info->decode = 1; /* supports decoding */
info->encode = 1; /* supports encoding */
info->size32 = 0; /* supports 32 bit size field */
strcpy(info->versionstr, "1.02"); /* version # */
strcpy(info->shorttypestr, "btr"); /* type */
strcpy(info->longtypestr, "BTree"); /* longtype */
}
return(info);
}

View file

@ -0,0 +1,62 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Copyright (C) Electronic Arts Canada Inc. 1995-2002. All rights reserved. */
#ifndef __BTRCODEX
#define __BTRCODEX 1
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __CODEX_H
#error "Include codex.h before btreecodex.h"
#endif
/****************************************************************/
/* BTR Codex */
/****************************************************************/
/* Information Functions */
CODEXABOUT *GCALL BTREE_about(void);
bool GCALL BTREE_is(const void *compresseddata);
/* Decode Functions */
int GCALL BTREE_size(const void *compresseddata);
#ifdef __cplusplus
int GCALL BTREE_decode(void *dest, const void *compresseddata, int *compressedsize=0);
#else
int GCALL BTREE_decode(void *dest, const void *compresseddata, int *compressedsize);
#endif
/* Encode Functions */
#ifdef __cplusplus
int GCALL BTREE_encode(void *compresseddata, const void *source, int sourcesize, int *opts=0);
#else
int GCALL BTREE_encode(void *compresseddata, const void *source, int sourcesize, int *opts);
#endif
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,165 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Copyright (C) Electronic Arts Canada Inc. 1995-2002. All rights reserved.
#ifndef __BTRREAD
#define __BTRREAD 1
#include <string.h>
#include "codex.h"
#include "btreecodex.h"
/****************************************************************/
/* Internal Functions */
/****************************************************************/
struct BTreeDecodeContext
{
signed char cluetbl[256];
unsigned char left[256];
unsigned char right[256];
unsigned char *d;
};
static void BTREE_chase(struct BTreeDecodeContext *DC, unsigned char node)
{
if (DC->cluetbl[node])
{
BTREE_chase(DC,DC->left[node]);
BTREE_chase(DC,DC->right[node]);
return;
}
*DC->d++ = node;
}
static int BTREE_decompress(unsigned char *packbuf,unsigned char *unpackbuf)
{
int node;
int i;
int nodes;
int clue;
int ulen;
unsigned char *s;
signed char c;
unsigned int type;
struct BTreeDecodeContext DC;
s = packbuf;
DC.d = unpackbuf;
ulen = 0L;
if (s)
{
type = ggetm(s,2);
s += 2;
/* (skip nothing for 0x46fb) */
if (type==0x47fb) /* skip ulen */
s += 3;
ulen = ggetm(s,3);
s += 3;
for (i=0;i<256;++i) /* 0 means a code is a leaf */
DC.cluetbl[i] = 0;
clue = *s++;
DC.cluetbl[clue] = 1; /* mark clue as special */
nodes = *s++;
for (i=0;i<nodes;++i)
{ node = *s++;
DC.left[node] = *s++;
DC.right[node] = *s++;
DC.cluetbl[node] = (signed char)-1;
}
for (;;)
{
node = (int) *s++;
c=DC.cluetbl[node];
if (!c)
{
*DC.d++ = (unsigned char) node;
continue;
}
if (c<0)
{
BTREE_chase(&DC,DC.left[node]);
BTREE_chase(&DC,DC.right[node]);
continue;
}
node = (int) *s++;
if (node)
{
*DC.d++ = (char) node;
continue;
}
break;
}
}
return(ulen);
}
/****************************************************************/
/* Information Functions */
/****************************************************************/
/* check for reasonable header: */
/* 46fb header */
bool GCALL BTREE_is(const void *compresseddata)
{
bool ok=false;
if (ggetm(compresseddata,2)==0x46fb
|| ggetm(compresseddata,2)==0x47fb)
ok = true;
return(ok);
}
/****************************************************************/
/* Decode Functions */
/****************************************************************/
int GCALL BTREE_size(const void *compresseddata)
{
int len=0;
if (ggetm(compresseddata,2)==0x46fb)
{
len = ggetm((char *)compresseddata+2,3);
}
else
{
len = ggetm((char *)compresseddata+2+3,3);
}
return(len);
}
int GCALL BTREE_decode(void *dest, const void *compresseddata, int *compressedsize)
{
return(BTREE_decompress((unsigned char *)compresseddata,(unsigned char *)dest));
}
#endif

View file

@ -0,0 +1,710 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Copyright (C) Electronic Arts Canada Inc. 1995-2002. All rights reserved.
#ifndef __BTRWRITE
#define __BTRWRITE 1
#include <string.h>
#include "codex.h"
#include "btreecodex.h"
/****************************************************************/
/* Internal Functions */
/****************************************************************/
#define BTREEWORD short
#define BTREECODES 256
#define BTREEBIGNUM 32000
#define BTREESLOPAGE 16384
struct BTREEMemStruct
{
char *ptr;
int len;
};
struct BTreeEncodeContext
{
unsigned int packbits;
unsigned int workpattern;
unsigned char *bufptr;
unsigned int ulen;
unsigned int masks[17];
unsigned char clueq[BTREECODES];
unsigned char right[BTREECODES];
unsigned char join[BTREECODES];
unsigned int plen;
unsigned char *bufbase;
unsigned char *bufend;
unsigned char *buffer;
unsigned char *buf1;
unsigned char *buf2;
};
static void BTREE_writebits(struct BTreeEncodeContext *EC,
struct BTREEMemStruct *dest,
unsigned int bitpattern,
unsigned int len)
{
if (len > 16)
{
BTREE_writebits(EC,dest,(unsigned int) (bitpattern>>16), len-16);
BTREE_writebits(EC,dest,(unsigned int) bitpattern, 16);
}
else
{
EC->packbits += len;
EC->workpattern += (bitpattern & EC->masks[len]) << (24-EC->packbits);
while (EC->packbits > 7)
{
*(dest->ptr+dest->len) = (unsigned char) (EC->workpattern >> 16);
++dest->len;
EC->workpattern = EC->workpattern << 8;
EC->packbits -= 8;
++EC->plen;
}
}
}
static void BTREE_adjcount(unsigned char *s, unsigned char *bend, BTREEWORD *count)
{
#ifdef __WATCOMC__
union
{
unsigned char b[4];
int w;
} i;
#define COUNTADJ(j) i.b[1] = i.b[0]; i.b[0] = *(s+j); ++count[i.w];
i.w = 0;
i.b[0] = (unsigned BTREEWORD) *s++;
#else
unsigned BTREEWORD i;
#define COUNTADJ(j) i = (BTREEWORD)(((i<<8) | *(s+j))); ++*(count+(int)i);
i = (unsigned BTREEWORD) *s++;
#endif
bend -= 16;
if (s<bend)
{
do
{
COUNTADJ(0);
COUNTADJ(1);
COUNTADJ(2);
COUNTADJ(3);
COUNTADJ(4);
COUNTADJ(5);
COUNTADJ(6);
COUNTADJ(7);
COUNTADJ(8);
COUNTADJ(9);
COUNTADJ(10);
COUNTADJ(11);
COUNTADJ(12);
COUNTADJ(13);
COUNTADJ(14);
COUNTADJ(15);
s += 16;
} while (s<bend);
}
bend += 16;
if (s<bend)
{
do
{
COUNTADJ(0);
++s;
} while (s<bend);
}
}
/* Clear 128 ints (256 words). Done as ints for speed. */
static void BTREE_clearcount(unsigned char *tryq,BTREEWORD *countbuf)
{
int zero=0;
int i;
int j;
int *intptr;
intptr = (int *) countbuf;
j = 256;
do
{
if (*tryq)
{
*tryq++ = 1;
i = 8;
do
{
*(intptr+0) = zero;
*(intptr+1) = zero;
*(intptr+2) = zero;
*(intptr+3) = zero;
*(intptr+4) = zero;
*(intptr+5) = zero;
*(intptr+6) = zero;
*(intptr+7) = zero;
*(intptr+8) = zero;
*(intptr+9) = zero;
*(intptr+10) = zero;
*(intptr+11) = zero;
*(intptr+12) = zero;
*(intptr+13) = zero;
*(intptr+14) = zero;
*(intptr+15) = zero;
intptr += 16;
} while (--i);
}
else
{
++tryq;
intptr += 128;
}
} while (--j);
}
static void BTREE_joinnodes(struct BTreeEncodeContext *EC,
unsigned char *cluep,
unsigned char *rightp,
unsigned char *joinp,
unsigned int clue)
{
unsigned char *s;
unsigned char *d;
unsigned char *bend;
unsigned int i;
/* pack file */
s = EC->bufbase;
if (s==EC->buf1)
{
d = EC->buf2;
}
else
{
d = EC->buf1;
}
EC->bufbase = d;
bend = EC->bufend;
*bend = (unsigned char) clue;
while (s<=bend)
{
while (!cluep[(unsigned int) (*d++ = *s++)])
;
i = (unsigned int) *(s-1);
if (cluep[i]==1)
{ if (*s==rightp[i])
{ *(d-1) = joinp[i];
++s;
}
}
else
{ if (cluep[i]==3)
{ *(d-1) = (unsigned char) clue;
*d++ = *(s-1);
}
else
*d++ = *s++;
}
}
EC->bufend = d-2;
}
/* find 48 most common nodes */
static unsigned int BTREE_findbest(BTREEWORD *countptr,
unsigned char *tryq,
unsigned int *bestn,
unsigned int *bestval,
int ratio)
{
unsigned int i;
unsigned int i1;
unsigned int i2;
unsigned int bestsize;
unsigned int i3;
unsigned int l;
bestsize = 1;
i = 3;
l = 0;
for (i2=0;i2<256;++i2)
{
if (tryq[i2])
{ for (i1=0;i1<256;++i1)
{
if (*countptr++>(BTREEWORD)i)
{
if (tryq[i1])
{ i = *(countptr-1);
i3=bestsize;
while (bestval[i3-1]<i)
{
bestn[i3] = bestn[i3-1];
bestval[i3] = bestval[i3-1];
--i3;
}
bestn[i3] = l+i1;
bestval[i3] = i;
if (bestsize<48)
++bestsize;
while (bestval[bestsize-1]<(bestval[1]/ratio))
--bestsize;
if (bestsize<48)
i = bestval[1]/ratio;
else
i = bestval[bestsize-1];
}
}
}
}
else
countptr += 256;
l += 256;
}
return(bestsize);
}
static void BTREE_treepack(struct BTreeEncodeContext *EC,
struct BTREEMemStruct *dest,
unsigned int passes,
unsigned int multimax,
unsigned int quick,
int zerosuppress)
{
unsigned char *bend;
unsigned char *ptr1;
unsigned char *treebuf;
BTREEWORD *count;
unsigned int i;
unsigned int i1;
int joinnode, leftnode, rightnode;
int ratio;
unsigned int hlen;
unsigned int domore;
unsigned int cost, save;
unsigned int tcost, tsave;
unsigned int clue;
unsigned int freeptr;
unsigned int count2[BTREECODES];
unsigned char tryq[BTREECODES];
unsigned char freeq[BTREECODES];
unsigned int bestsize;
unsigned char bestjoin[BTREECODES];
unsigned int bestn[BTREECODES];
unsigned int bestval[BTREECODES];
unsigned int bt_size;
unsigned int bt_node[BTREECODES];
unsigned int bt_left[BTREECODES];
unsigned int bt_right[BTREECODES];
unsigned int sortptr[BTREECODES];
int treebufsize;
int buf1size;
int buf2size;
// 3/2 allows for worst case, where 2nd most popular
treebufsize = 65536L*sizeof(BTREEWORD); /* 131K */
buf1size = EC->ulen*3/2+(int)BTREESLOPAGE;
buf2size = EC->ulen*3/2+(int)BTREESLOPAGE;
treebuf = (unsigned char *) galloc(treebufsize);
if (!treebuf)
return; /* failure Insufficient memory for work buffer */
EC->buf1 = (unsigned char *) galloc(buf1size);
if (!EC->buf1)
return; /* failure Insufficient memory for work buffer */
EC->buf2 = (unsigned char *) galloc(buf2size);
if (!EC->buf2)
return; /* failure Insufficient memory for work buffer */
memcpy(EC->buf1, EC->buffer, EC->ulen); /* copy to scratch buffer */
EC->buffer = EC->buf1;
EC->bufptr = EC->buf1+EC->ulen;
EC->bufbase = EC->buffer;
EC->bufend = EC->bufptr;
count = (BTREEWORD *) treebuf;
if (quick) ratio = quick;
else ratio = 2;
/*** count file ***/
for (i=0; i<BTREECODES; ++i)
count2[i] = 0;
i1 = 0;
bend = EC->bufptr;
ptr1 = EC->buffer;
while (ptr1<bend)
{ i = (unsigned int) *ptr1++;
i1 = i1 + i;
++count2[i];
}
/* init clue array */
for (i=0;i<BTREECODES;++i)
EC->clueq[i] = 0;
/* get a clue byte (forced) */
for(i=0; i<BTREECODES; ++i)
{ freeq[i] = 1;
if (count2[i]>3)
tryq[i] = 1;
else
tryq[i] = 0;
}
/* don't use 0 for clue or node (so that [clue][0] is reserved for EOF) */
count2[0] = BTREEBIGNUM; /* don't use for clue or node */
/* asciiz btree suppress packing of 0..31 */
if (zerosuppress)
{
for (i=0; i<32; ++i)
{
count2[i] = BTREEBIGNUM; /* don't use for nodes */
tryq[i] = 0; /* don't try to pack */
freeq[i] = 0; /* don't try to pack */
}
}
/* sort codes with least used codes first */
/* primary key is count, secondary key is higher code */
/* registers vars usage
i - code
i1 - done flag/swap temp
*/
for (i=0;i<BTREECODES;++i)
sortptr[i] = i;
i1=1;
while (i1)
{
i1 = 0;
for (i=1; i<BTREECODES; ++i)
{
if (count2[sortptr[i]]<count2[sortptr[i-1]] || ((count2[sortptr[i]]==count2[sortptr[i-1]]) && (sortptr[i]>sortptr[i-1])))
{
i1 = sortptr[i];
sortptr[i] = sortptr[i-1];
sortptr[i-1] = i1;
i1 = 1;
}
}
}
freeptr = 0;
i = sortptr[freeptr++];
clue = (unsigned char) i;
freeq[i] = 0;
tryq[i] = 0;
EC->clueq[i] = 3;
if (count2[i])
BTREE_joinnodes(EC,EC->clueq, EC->right, EC->join, clue);
EC->clueq[i] = 2;
/* init best array */
bestval[0] = (unsigned int) -1;
/* count file (pass 2) */
bt_size = 0;
domore = passes;
while (domore)
{
/* clear count array */
BTREE_clearcount(tryq,count);
/* do an adjacency count */
ptr1 = EC->bufbase;
bend = EC->bufend;
BTREE_adjcount(ptr1, bend, count);
/* find most common nodes */
bestsize = BTREE_findbest(count,tryq,bestn,bestval,ratio);
/* decide which nodes should be joined to what */
domore = 0;
if (bestsize>1)
{
tcost = tsave = 0;
i = i1 = domore = 1;
while (domore)
{
leftnode = (bestn[i]>>8)&255;
rightnode = bestn[i]&255;
if ((tryq[leftnode]==1) && (tryq[rightnode]==1))
{
domore = 0;
while ((freeptr<BTREECODES) && (!freeq[sortptr[freeptr]]))
++freeptr;
if (freeptr<BTREECODES)
{
joinnode = sortptr[freeptr];
cost = 3+count2[joinnode];
save = bestval[i];
if (cost<save)
{
tcost += cost;
tsave += save;
bestjoin[i1] = (unsigned char) joinnode;
bestn[i1] = bestn[i];
++i1;
freeq[joinnode] = 0;
tryq[joinnode] = 2;
EC->clueq[joinnode] = 3;
freeq[leftnode] = 0;
tryq[leftnode] = 2;
EC->clueq[leftnode] = 1;
EC->right[leftnode] = (unsigned char) rightnode;
EC->join[leftnode] = (unsigned char) joinnode;
freeq[rightnode] = 0;
tryq[rightnode] = 2;
bt_node[bt_size] = joinnode;
bt_left[bt_size] = leftnode;
bt_right[bt_size] = rightnode;
++bt_size;
if (i1<=multimax)
domore = 1;
}
}
}
++i;
if (i>=bestsize)
domore = 0;
}
bestsize = i1;
if (bestsize>1)
{
/* multijoin nodes */
BTREE_joinnodes(EC,EC->clueq, EC->right, EC->join, clue);
/* restore clue table */
for(i=1;i<bestsize;++i)
{ leftnode = (bestn[i]>>8)&255;
joinnode = bestjoin[i];
EC->clueq[leftnode] = EC->clueq[joinnode] = 0;
}
domore = --passes;
}
}
}
EC->bufptr = EC->bufend;
/* write header */
BTREE_writebits(EC,dest,(unsigned int) clue, 8); /* clue byte */
BTREE_writebits(EC,dest,(unsigned int) bt_size, 8); /* tree size */
for (i=0; i<bt_size;++i)
{ BTREE_writebits(EC,dest,(unsigned int) bt_node[i], 8);
BTREE_writebits(EC,dest,(unsigned int) bt_left[i], 8);
BTREE_writebits(EC,dest,(unsigned int) bt_right[i], 8);
}
hlen = EC->plen;
/*** write packed file ***/
ptr1 = EC->bufbase;
bend = EC->bufend;
while (ptr1<bend)
BTREE_writebits(EC,dest,(unsigned int) *ptr1++, 8);
BTREE_writebits(EC,dest,(unsigned int) clue, 8);
BTREE_writebits(EC,dest,(unsigned int) 0, 8);
BTREE_writebits(EC,dest,0L,7); /* flush bits */
gfree(EC->buf2);
gfree(EC->buf1);
gfree(treebuf);
}
static int BTREE_compressfile(struct BTreeEncodeContext *EC,
struct BTREEMemStruct *infile,
struct BTREEMemStruct *outfile,
int ulen,
int zerosuppress)
{
unsigned int i;
unsigned int passes;
unsigned int multimax;
int flen;
/* set defaults */
EC->packbits = 0;
EC->workpattern = 0L;
passes = 256;
multimax = 32;
EC->masks[0] = 0;
for (i=1;i<17;++i)
EC->masks[i] = (EC->masks[i-1] << 1) + 1;
/* read in a source file */
EC->buffer = (unsigned char *) (infile->ptr);
flen = infile->len;
EC->ulen = flen;
EC->bufptr = EC->buffer + flen;
/* pack a file */
outfile->ptr = outfile->ptr;
outfile->len = 0L;
EC->packbits = 0;
EC->workpattern = 0L;
EC->plen = 0L;
/* write standard header stuff (type/signature/ulen/adjust) */
/* simple fb6 header */
if (ulen==infile->len)
{
BTREE_writebits(EC,outfile,(unsigned int) 0x46fb, 16);
BTREE_writebits(EC,outfile,(unsigned int) infile->len, 24);
}
/* composite fb6 header */
else
{
BTREE_writebits(EC,outfile,(unsigned int) 0x47fb, 16);
BTREE_writebits(EC,outfile,(unsigned int) ulen, 24);
BTREE_writebits(EC,outfile,(unsigned int) infile->len, 24);
}
BTREE_treepack(EC,outfile,passes, multimax, 0, zerosuppress);
return(outfile->len);
}
/****************************************************************/
/* Encode Function */
/****************************************************************/
int GCALL BTREE_encode(void *compresseddata, const void *source, int sourcesize, int *opts)
{
int plen;
struct BTREEMemStruct infile;
struct BTREEMemStruct outfile;
struct BTreeEncodeContext EC;
int opt=0;
if (opts)
opt = opts[0];
infile.ptr = (char *)source;
infile.len = sourcesize;
outfile.ptr = (char *)compresseddata;
outfile.len = sourcesize;
plen = BTREE_compressfile(&EC,&infile, &outfile, sourcesize, opt);
return(plen);
}
#endif

View file

@ -0,0 +1,123 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Copyright (C) Electronic Arts Canada Inc. 1995-2002. All rights reserved. */
/*------------------------------------------------------------------*/
/* */
/* Compression Decompression Exchange(CODEX) v2.00 */
/* */
/* by FrANK G. Barchard, EAC */
/* */
/* Header Module - Oct 15, 2001 */
/* */
/*------------------------------------------------------------------*/
/* */
/* Version Date SE History */
/* ------- ------ -- ------- */
/* 1.00 990824 FB codex API seperated from huff tool */
/* 1.01 010427 FB fb6 32 bit size header */
/* 1.02 011011 FB c++ defaults */
/* 2.00 011015 FB bool, dest/source, new about struct,no QPUBLIC */
/* */
/*------------------------------------------------------------------*/
#ifndef __CODEX_H
#define __CODEX_H 1
#if defined(_MSC_VER)
#pragma once
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define CODEX_VERSION 200
/****************************************************************/
/* Data Types */
/****************************************************************/
/* Info structure describing bitmaps */
typedef struct
{
int signature; /* signature of codex ie 'tga ' (optional) */
int size; /* size of CODEXABOUT structure */
int version; /* version number of CODEXABOUT structure (200) */
unsigned int decode :1; /* supports decoding */
unsigned int encode :1; /* supports encoding */
unsigned int size32 :1; /* support 32 bit size field */
unsigned int pad :29;
char versionstr[8]; /* version number of codex module ie 1.00 */
char shorttypestr[8]; /* 3 or 4 character type string ie ref */
char longtypestr[16]; /* full name of data format ie Refpack */
} CODEXABOUT;
#define QMAKEID(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d))
#if !defined(GCALL)
#if defined(_MSC_VER) && !defined(_XBOX)
#define GCALL __stdcall
#else
#define GCALL
#endif
#endif
typedef struct QFUNCTIONS
{
CODEXABOUT * (GCALL * CODEX_about)(void);
bool (GCALL * CODEX_is)(const void *compressed);
int (GCALL * CODEX_size)(const void *compressed);
int (GCALL * CODEX_decode)(void *dest, const void *source, int *sourcesizeptr);
int (GCALL * CODEX_encode)(void *dest, const void *source, int sourcesize, int *opts);
} QFUNCTIONS;
extern struct QFUNCTIONS qfunctions[];
/****************************************************************/
/* Codex Module Example Prototypes */
/****************************************************************/
#include "gimex.h" /* for memory IO */
/* Information Functions */
CODEXABOUT *GCALL CODEX_about(void);
bool GCALL CODEX_is(const void *source);
int GCALL CODEX_size(const void *source);
/* Decode/Encode Functions */
#ifdef __cplusplus
int GCALL CODEX_decode(void *dest, const void *source, int *sourcesizeptr=0);
int GCALL CODEX_encode(void *dest, const void *source, int sourcesize, int *opts=0);
#else
int GCALL CODEX_decode(void *dest, const void *source, int *sourcesizeptr);
int GCALL CODEX_encode(void *dest, const void *source, int sourcesize, int *opts);
#endif
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,497 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Copyright (C) Electronic Arts Canada Inc. 1994-2002. All rights reserved. */
/* ABSTRACT
gimex.h - Primary header file for the GIMEX API.
@ */
/*------------------------------------------------------------------*/
/* */
/* Graphics IMport EXport API v3.46 */
/* */
/* by Frank Barchard, EAC */
/* */
/* Header Module - Dec 05, 2002 */
/* */
/*------------------------------------------------------------------*/
/* */
/* Version Date SE History */
/* ------- ------ -- ------- */
/* 3.01 ------ FB gref void * */
/* 3.02 ------ FB const GIMEX_write */
/* 3.10 ------ FB 16 bpp import */
/* 3.11 ------ FB gbitmap->image void * */
/* 3.12 ------ FB about importstream, exportstream, maxredbits et*/
/* 3.13 ------ FB about->movie */
/* 3.14 ------ FB GFUNCTIONS table declared */
/* 3.15 ------ FB about->mipmap */
/* 3.16 ------ FB about->font */
/* 3.17 ------ FB linux use intel ordered ARGB */
/* 3.20 ------ FB 64 bit file system (GPOS), removed ARGB16 */
/* 3.21 ------ FB 64 bit backwards compatibility with 32 */
/* 3.22 ------ FB 32 bit file implementation for efficiency */
/* 3.23 ------ FB bitfield warning removed */
/* 3.24 010426 FB GIMEX_read/write take void * prototyped here */
/* 3.25 010427 FB about width/height max,align and obsolete */
/* 3.26 010501 FB INT64 for gcc 2.95.3 on ps2 use long */
/* 3.27 010504 FB removed _ARGB_T - use ARGB. add GC reference */
/* 3.30 010614 LC update for 330 release */
/* 3.31 010628 FB GIMEX_COMMENT_SIZE 1024 */
/* 3.32 011009 FB about->file64 for 64 bit file sizes */
/* 3.40 011022 FB bool returns, ggetm inline, 512 GIMEX_FRAME... */
/* 3.41 020225 FB metalbin 240 on ps2 */
/* 3.44 020307 FB bumped for next release. no api change */
/* 3.45 020927 FB geti 3 bounds safe/purify */
/* 3.46 021205 FB about firstextension */
/* */
/*------------------------------------------------------------------*/
#ifndef __GIMEX_H
#define __GIMEX_H 1
#if defined(_MSC_VER)
#pragma once
#pragma warning(disable : 4100)
/* warning C4100: unreferenced parameter */
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define GIMEX_VERSION 346
#define GIMEX_PATCH 0
/****************************************************************************/
/* Data Types */
/****************************************************************************/
/* ARGB structure used for palettes/pixels */
// experimental float channel
#ifdef GIMEXFLOAT
typedef float GCHANNEL;
#else
typedef unsigned char GCHANNEL;
#endif
#ifndef ARGB
#define ARGB ARGB
#if defined(_MSC_VER) || defined(__i386__)
typedef struct
{
GCHANNEL b,g,r,a;
} ARGB;
#elif defined(__R5900) || defined(SGI) /* PS2 */
typedef struct
{
GCHANNEL r,g,b,a;
} ARGB;
#else /* GameCube/Mac */
typedef struct
{
GCHANNEL a,r,g,b;
} ARGB;
#endif
#endif
#if !defined(GPOS)
#if defined(_MSC_VER)
typedef __int64 GPOS;
#elif defined(__R5900)
typedef long GPOS;
#else
typedef long long GPOS;
#endif
#endif
/* Info structure describing bitmaps */
#define GIMEX_FRAMENAME_SIZE 512
#define GIMEX_COMMENT_SIZE 1024
#define GIMEX_COLOURTBL_SIZE 256
#define GIMEX_HOTSPOTTBL_SIZE 1024
#define GIMEX_HOTSPOTTBL_VALUES 2
typedef struct
{
int signature; /* signature of gimex ie 'tga ' (optional) */
int size; /* size of GINFO structure */
int version; /* version number of GINFO structure (300) */
int framenum; /* current frame */
int width; /* width of bitmap in pixels */
int height; /* height of bitmap in pixels */
int bpp; /* bits per pixel (8, 16 or 32) */
int originalbpp; /* bits per pixel in original image (1 to 32) */
int startcolour; /* first colour in palette */
int numcolours; /* number of colours in original indexed palette */
ARGB colourtbl[GIMEX_COLOURTBL_SIZE]; /* 8 bit palette */
int subtype; /* internal format sub-type 0-default */
int packed; /* type of packing on original image. 0 none, 1 run, n other */
int quality; /* quality of lossy packing 0..100 */
int framesize; /* size of frame in bytes */
int alphabits; /* number of bits in alpha channel */
int redbits; /* number of bits in red channel */
int greenbits; /* number of bits in green channel */
int bluebits; /* number of bits in blue channel */
int centerx; /* center point relative to upper left corner */
int centery;
int defaultx; /* default coordinate point */
int defaulty;
int numhotspots; /* number of hot spots defined */
char framename[GIMEX_FRAMENAME_SIZE]; /* null terminated name of frame/image */
char comment[GIMEX_COMMENT_SIZE]; /* null terminated multiline user comment */
int hotspottbl[GIMEX_HOTSPOTTBL_SIZE][GIMEX_HOTSPOTTBL_VALUES];/* up to 256 hot spots, XY pairs relative to upperleft */
float dpi; /* dots per inch ie 72.0 */
float fps; /* frame per second (one over duration). 15.0 is typical */
int reserved[3]; /* reserved for future use - set to zero */
} GINFO;
typedef struct GSTREAM GSTREAM; /* handle used for file functions */
typedef struct
{
int signature; /* signature of gimex ie '.tga' (optional) */
int size; /* size of GINSTANCE structure */
int frames; /* Number of frames in file */
int framenum; /* current frame (optional) */
GSTREAM *gstream; /* stream pointer for file */
void *gref; /* gimex reference to additional memory used by module (optional) */
} GINSTANCE;
/* Info structure describing bitmaps */
#define MAXMACTYPES 8
#define MAXEXTENSIONS 8
#define GIMEX_EXTENSION_SIZE 8
#define GIMEX_AUTHORSTR_SIZE 32
#define GIMEX_VERSIONSTR_SIZE 8
#define GIMEX_SHORTTYPESTR_SIZE 8
#define GIMEX_WORDTYPESTR_SIZE 16
#define GIMEX_LONGTYPESTR_SIZE 32
typedef struct
{
int signature; /* signature of gimex ie 'tga ' (optional) */
int size; /* size of GABOUT structure */
int version; /* version number of GABOUT structure (200) */
unsigned int canimport :1; /* supports importing */
unsigned int canexport :1; /* supports exporting */
unsigned int importpacked :2; /* max import packed field 0..3 */
unsigned int exportpacked :2; /* max export packed field 0..3 */
unsigned int import8 :1; /* supports importing 8 bit indexed */
unsigned int export8 :1; /* supports exporting 8 bit indexed */
unsigned int import32 :1; /* supports importing 32 bit direct rgb */
unsigned int export32 :1; /* supports exporting 32 bit direct rgb */
unsigned int multiframe :1; /* supports multiple frames */
unsigned int multifile :1; /* format requires additional files or resource fork */
unsigned int multisize :1; /* supports different size per frame */
unsigned int framebuffer :1; /* module requires memory to buffer entire frame */
unsigned int external :1; /* uses external resources */
unsigned int usesfile :1; /* module is file based vs ads/printer/generator */
unsigned int globalpalette :1; /* limited to a single palette per file */
unsigned int greyscale :1; /* use maxcolours for number of levels */
unsigned int startcolour :1; /* supports start colour */
unsigned int dotsubtype :1; /* subtype based on extension */
unsigned int resizable :1; /* read will respect ginfo width & height */
unsigned int reserved2 :1; /* reserved for future use */
unsigned int reserved3 :1; /* reserved for future use */
unsigned int importstream :1; /* supports open with GIMEX_NOFRAMECOUNT */
unsigned int exportstream :1; /* will ignore frame count on export */
unsigned int movie :1; /* this is a movie format (as opposed to mipmaps, fonts or multipage) */
unsigned int mipmap :1; /* mipmaps are supported and will constrain sizes */
unsigned int font :1; /* this is a font format */
unsigned int obsolete :1; /* this format is obsolete */
unsigned int file64 :1; /* this format supports 64 bit file sizes */
unsigned int firstextension:1; /* use first extension when assigning default name */
unsigned int pad :1; /* pad bitfield to 32 bit boundary for inter compiler compatibility */
int maxcolours; /* only use in 8 bit, 0 if module does not care */
int maxframename; /* maximum characters in ginfo framename */
int defaultquality; /* default pack quality */
int mactype[MAXMACTYPES]; /* mac file system types used */
char extensions[MAXEXTENSIONS][GIMEX_EXTENSION_SIZE]; /* null terminated extensions with '.' */
char authorstr[GIMEX_AUTHORSTR_SIZE]; /* name of gimex module author */
char versionstr[GIMEX_VERSIONSTR_SIZE]; /* version number of gimex module ie 1.00 */
char shorttypestr[GIMEX_SHORTTYPESTR_SIZE]; /* 3 or 4 character type string ie TGA */
char wordtypestr[GIMEX_WORDTYPESTR_SIZE]; /* single word type string ie Targa */
char longtypestr[GIMEX_LONGTYPESTR_SIZE]; /* full name of data format ie True Vision Targa */
unsigned int maxalphabits:8; /* maximum supported number of bits in alpha channel */
unsigned int maxredbits :8; /* maximum supported number of bits in red channel */
unsigned int maxgreenbits:8; /* maximum supported number of bits in green channel */
unsigned int maxbluebits :8; /* maximum supported number of bits in blue channel */
unsigned int maxwidth; /* maximum width in pixels */
unsigned int maxheight; /* maximum height in pixels */
unsigned int alignwidth; /* width must be multiple of this in pixels */
unsigned int alignheight; /* height must be multiple of this in pixels */
unsigned int pad2[4];
} GABOUT;
/* Bitmap structure (optional) */
typedef struct
{
GINFO *ginfo;
void *image;
int rowbytes;
} GBITMAP;
#ifndef GMAKEID
#define GMAKEID(a,b,c,d) (((int)(a)<<24)|((int)(b)<<16)|((int)(c)<<8)|(int)(d))
#endif
#ifndef gmin
#define gmin(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef gmax
#define gmax(a,b) ((a)>(b)?(a):(b))
#endif
#if !defined(GCALL)
#if defined(_MSC_VER) && !defined(_XBOX)
#define GCALL __stdcall
#else
#define GCALL
#endif
#endif
#if !defined(__cplusplus) && !defined(bool)
#define bool bool
typedef enum{ False=0x0, True=0x1}bool;
#endif
typedef struct GFUNCTIONS
{
GABOUT * (GCALL * GIMEX_about)(void);
int (GCALL * GIMEX_is)(GSTREAM *g);
int (GCALL * GIMEX_open)(GINSTANCE **gx, GSTREAM *g, const char *pathname,bool framecountflag);
GINFO * (GCALL * GIMEX_info)(GINSTANCE *gx, int framenum);
int (GCALL * GIMEX_read)(GINSTANCE *gx, GINFO *ginfo, char *dest, int rowbytes);
int (GCALL * GIMEX_close)(GINSTANCE *gx);
int (GCALL * GIMEX_wopen)(GINSTANCE **gx, GSTREAM *g, const char *pathname, int numframes);
int (GCALL * GIMEX_write)(GINSTANCE *gx, const GINFO *ginfo, char *source, int rowbytes);
int (GCALL * GIMEX_wclose)(GINSTANCE *gx);
} GFUNCTIONS;
extern struct GFUNCTIONS gfunctions[];
/****************************************************************************/
/* Gimex Module Example Prototypes */
/****************************************************************************/
/* valid values to be passed to framecountflag parameter for the GIMEX_open function */
#ifdef __cplusplus
#define GIMEX_FRAMECOUNT true
#define GIMEX_NOFRAMECOUNT false
#else
#define GIMEX_FRAMECOUNT 0x1
#define GIMEX_NOFRAMECOUNT 0x0
#endif
/* Information Functions */
GABOUT *GCALL GIMEX_about(void);
int GCALL GIMEX_is(GSTREAM *g);
/* Import Functions */
#ifdef __cplusplus
int GCALL GIMEX_open(GINSTANCE **gx, GSTREAM *g, const char *pathname, bool framecountflag=GIMEX_FRAMECOUNT);
#else
int GCALL GIMEX_open(GINSTANCE **gx, GSTREAM *g, const char *pathname, bool framecountflag);
#endif
GINFO * GCALL GIMEX_info(GINSTANCE *gx, int framenum);
bool GCALL GIMEX_read(GINSTANCE *gx, const GINFO *ginfo, void *dest, int rowbytes);
bool GCALL GIMEX_close(GINSTANCE *gx);
/* Example Export Functions */
bool GCALL GIMEX_wopen(GINSTANCE **gx, GSTREAM *g, const char *pathname, int numframes);
bool GCALL GIMEX_write(GINSTANCE *gx, const GINFO *ginfo, const void *source, int rowbytes);
bool GCALL GIMEX_wclose(GINSTANCE *gx);
/****************************************************************************/
/* Application Module Prototypes */
/****************************************************************************/
/* File Stream Functions */
GSTREAM * GCALL gopen(const char *pathname);
GSTREAM * GCALL gwopen(const char *pathname);
bool GCALL gclose(GSTREAM *g);
int GCALL gread(GSTREAM *g, void *buf, int size);
int GCALL gwrite(GSTREAM *g, const void *buf, int size);
bool GCALL gseek(GSTREAM *g, GPOS offset);
GPOS GCALL glen(GSTREAM *g);
GPOS GCALL gtell(GSTREAM *g);
/* Memory Functions */
#if !defined(galloc)
#include <stdlib.h>
#define galloc malloc
//void * GCALL galloc(int size);
#endif
//bool GCALL gfree(void *memptr);
#define gfree free
#ifdef __cplusplus
}
#endif
/****************************************************************************/
/* Inline C++ (cross platform) */
/****************************************************************************/
#ifdef _MSC_VER
#pragma warning(disable: 4514) // unreferenced inline function has been removed
#endif
/* get motorola memory */
static __inline unsigned int ggetm(const void *src, int bytes)
{
if (bytes==1)
return (unsigned int) *(const unsigned char *) src;
#if defined(__APPLE__) || (defined(__MWERKS__) && defined(__PPCGEKKO__))
else if (bytes==2)
return (unsigned int) *(const unsigned short *) src;
else if (bytes==4)
return (unsigned int) *(const unsigned int *) src;
#else
else if (bytes==2)
return (((unsigned int) *(const unsigned char *) src)<<8) | (((unsigned int) *((const unsigned char *) src+1)));
else if (bytes==4)
return (((unsigned int) *(const unsigned char *) src)<<24) | (((unsigned int) *((const unsigned char *) src+1))<<16) | (((unsigned int) *((const unsigned char *) src+2))<<8) | (((unsigned int) *((const unsigned char *) src+3)));
#endif
else if (bytes==3)
return (((unsigned int) *(const unsigned char *) src)<<16) | (((unsigned int) *((const unsigned char *) src+1))<<8) | (((unsigned int) *((const unsigned char *) src+2)));
return 0;
}
/* get intel memory */
static __inline unsigned int ggeti(const void *src, int bytes)
{
if (bytes==1)
return (unsigned int) *(const unsigned char *) src;
#if defined(_MSC_VER) || defined(__i386__)
else if (bytes==2)
return (unsigned int) *(const unsigned short *) src;
else if (bytes==4)
return (unsigned int) *(const unsigned int *) src;
#else
else if (bytes==2)
return (((unsigned int) *((const unsigned char *) src+1))<<8) | (((unsigned int) *(const unsigned char *) src+0));
else if (bytes==4)
return (((unsigned int) *((const unsigned char *) src+3))<<24) | (((unsigned int) *((const unsigned char *) src+2))<<16) | (((unsigned int) *((const unsigned char *) src+1))<<8) | (((unsigned int) *(const unsigned char *) src+0));
#endif
else if (bytes==3)
return (((unsigned int) *((const unsigned char *) src+2))<<16) | (((unsigned int) *((const unsigned char *) src+1))<<8) | (((unsigned int) *(const unsigned char *) src+0));
return 0;
}
/* put motorolla memory */
static __inline void gputm(void *dst, unsigned int data, int bytes)
{
if (bytes==1)
{
((unsigned char *) dst)[0] = (unsigned char) data;
}
#if defined(__APPLE__) || (defined(__MWERKS__) && defined(__PPCGEKKO__))
else if (bytes==2)
{
((unsigned short *) dst)[0] = (unsigned short) data;
}
else if (bytes==4)
{
((unsigned int *) dst)[0] = (unsigned int) data;
}
#else
else if (bytes==2)
{
((unsigned char *) dst)[0] = (unsigned char) (data>>8);
((unsigned char *) dst)[1] = (unsigned char) data;
}
else if (bytes==4)
{
((unsigned char *) dst)[0] = (unsigned char) (data>>24);
((unsigned char *) dst)[1] = (unsigned char) (data>>16);
((unsigned char *) dst)[2] = (unsigned char) (data>>8);
((unsigned char *) dst)[3] = (unsigned char) data;
}
#endif
else if (bytes==3)
{
((unsigned char *) dst)[0] = (unsigned char) (data>>16);
((unsigned char *) dst)[1] = (unsigned char) (data>>8);
((unsigned char *) dst)[2] = (unsigned char) data;
}
}
/* put intel memory */
static __inline void gputi(void *dst, unsigned int data, int bytes)
{
if (bytes==1)
{
((unsigned char *) dst)[0] = (unsigned char) data;
}
#if defined(_MSC_VER) || defined(__i386__)
else if (bytes==2)
{
((unsigned short *) dst)[0] = (unsigned short) data;
}
else if (bytes==4)
{
((unsigned int *) dst)[0] = (unsigned int) data;
}
#else
else if (bytes==2)
{
((unsigned char *) dst)[0] = (unsigned char) data;
((unsigned char *) dst)[1] = (unsigned char) (data>>8);
}
else if (bytes==4)
{
((unsigned char *) dst)[0] = (unsigned char) data;
((unsigned char *) dst)[1] = (unsigned char) (data>>8);
((unsigned char *) dst)[2] = (unsigned char) (data>>16);
((unsigned char *) dst)[3] = (unsigned char) (data>>24);
}
#endif
else if (bytes==3)
{
((unsigned char *) dst)[0] = (unsigned char) data;
((unsigned char *) dst)[1] = (unsigned char) (data>>8);
((unsigned char *) dst)[2] = (unsigned char) (data>>16);
}
}
#endif /* __GIMEX_H */
/* END ABSTRACT */

View file

@ -0,0 +1,88 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Copyright (C) Electronic Arts Canada Inc. 1995-2002. All rights reserved.
/* ABSTRACT */
/*------------------------------------------------------------------*/
/* */
/* Huffman - Huffman with Runlength Codex */
/* */
/* by FrANK G. Barchard, EAC */
/* */
/*------------------------------------------------------------------*/
/* */
/* Version Date SE History: */
/* ------- ---- -- -------- */
/* 1.00 950108 FB based on huff and ref codex */
/* 1.01 950316 FB delta huff and delta delta huff */
/* 1.02 950317 FB quick table version (used in wing3) */
/* 1.03 950626 FB allocate context instead of on stack */
/* 1.04 010608 ID forgot to undelta big size buffer fix */
/* */
/*------------------------------------------------------------------*/
/* */
/* Module Notes: */
/* ------------- */
/* Reentrant */
/* Files: hufread.c hufwrite.c hufcodex.h */
/* */
/*------------------------------------------------------------------*/
/* */
/* Format Notes: */
/* ------------- */
/* *30fb fb6 huff 6.1 EOF only huff */
/* *31fb fb6 huff 6.1 ' ' ' composite */
/* *32fb fb6 huff 6.1 EOF only speed */
/* *33fb fb6 huff 6.1 ' ' ' composite */
/* *34fb fb6 huff 6.1 EOF only acceleration */
/* *35fb fb6 huff 6.1 ' ' ' composite */
/* */
/*------------------------------------------------------------------*/
/* END ABSTRACT */
#include <string.h>
#include "codex.h"
#include "huffcodex.h"
/****************************************************************/
/* Information Functions */
/****************************************************************/
CODEXABOUT *GCALL HUFF_about(void)
{
CODEXABOUT *info;
info = (CODEXABOUT *) galloc(sizeof(CODEXABOUT));
if (info)
{
memset(info, 0, sizeof(CODEXABOUT));
info->signature = QMAKEID('H','U','F','F');
info->size = sizeof(CODEXABOUT);
info->version = 200; /* codex version number (200) */
info->decode = 1; /* supports decoding */
info->encode = 1; /* supports encoding */
info->size32 = 0; /* supports 32 bit size field */
strcpy(info->versionstr, "1.04"); /* version # */
strcpy(info->shorttypestr, "huff"); /* type */
strcpy(info->longtypestr, "Huffman"); /* longtype */
}
return(info);
}

View file

@ -0,0 +1,74 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Copyright (C) Electronic Arts Canada Inc. 1995-2002. All rights reserved. */
#ifndef __HUFCODEX
#define __HUFCODEX 1
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __CODEX_H
#error "Include codex.h before huffcodex.h"
#endif
/****************************************************************/
/* HUF Codex */
/****************************************************************/
/* Information Functions */
CODEXABOUT *GCALL HUFF_about(void);
bool GCALL HUFF_is(const void *compresseddata);
/* Decode Functions */
int GCALL HUFF_size(const void *compresseddata);
#ifdef __cplusplus
int GCALL HUFF_decode(void *dest, const void *compresseddata, int *compressedsize=0);
#else
int GCALL HUFF_decode(void *dest, const void *compresseddata, int *compressedsize);
#endif
/* Encode Functions */
#ifdef __cplusplus
int GCALL HUFF_encode(void *compresseddata, const void *source, int sourcesize, int *opts=0);
#else
int GCALL HUFF_encode(void *compresseddata, const void *source, int sourcesize, int *opts);
#endif
/****************************************************************/
/* Internal */
/****************************************************************/
#ifndef qmin
#define qmin(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef qmax
#define qmax(a,b) ((a)>(b)?(a):(b))
#endif
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,570 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Copyright (C) Electronic Arts Canada Inc. 1995-2002. All rights reserved.
#ifndef __HUFREAD
#define __HUFREAD 1
#include <string.h>
#include "codex.h"
#include "huffcodex.h"
#if defined(_MSC_VER)
#pragma warning(push,1)
#endif
/****************************************************************/
/* Internal Functions */
/****************************************************************/
struct HuffDecodeContext
{
unsigned char *s;
int bitsleft;
unsigned int bits;
};
/****************************************************************/
/* Huffman Unpacker */
/****************************************************************/
static int ZERO=0;
#define GET16BITS() \
bitsunshifted = qs[0] | (bitsunshifted << 8);\
bitsunshifted = qs[1] | (bitsunshifted << 8);\
qs += 2;
#define SQgetbits(v,n)\
if (n) \
{ \
v = bits >> (32-(n));\
bits <<= (n);\
bitsleft -= (n);\
} \
\
if (bitsleft<0)\
{\
GET16BITS() \
\
bits = bitsunshifted<<(-bitsleft);\
bitsleft += 16;\
}
#define SQgetnum(v) \
if ((int)bits<0)\
{\
SQgetbits(v,3);\
v -= 4;\
}\
else\
{\
int n;\
unsigned int v1;\
\
if (bits>>16)\
{\
n=2;\
do\
{\
bits <<= 1; \
++n;\
}\
while ((int)bits>=0);\
bits <<= 1;\
bitsleft -= (n-1);\
SQgetbits(v,ZERO);\
}\
else\
{\
n=2;\
do\
{\
++n;\
SQgetbits(v,1);\
}\
while (!v);\
}\
if (n>16)\
{\
SQgetbits(v,n-16);\
SQgetbits(v1,16);\
v = (v1|(v<<16))+(1<<n)-4;\
}\
else\
{\
SQgetbits(v,n);\
v = (v+(1<<n)-4);\
}\
}
/* note: requires 'amt' be a multiple of 16 */
#define SQmemset(ptr, val, amt) \
{\
int i = amt/16;\
int intval = (val<<24)|(val<<16)|(val<<8)|val;\
int* lptr = (int*)ptr;\
\
while (i)\
{\
lptr[0] = intval;\
lptr[1] = intval;\
lptr[2] = intval;\
lptr[3] = intval;\
lptr += 4;\
--i;\
}\
}\
static int HUFF_decompress(unsigned char *packbuf, unsigned char *unpackbuf)
{
unsigned int type;
unsigned char clue;
int ulen;
unsigned int cmp;
int bitnum=0;
int cluelen=0;
unsigned char *qs;
unsigned char *qd;
unsigned int bits;
unsigned int bitsunshifted=0;
int numbits;
int bitsleft;
unsigned int v;
qs = packbuf;
qd = unpackbuf;
ulen = 0L;
if (qs)
{
{
int mostbits;
int i;
int bitnumtbl[16];
unsigned int deltatbl[16];
unsigned int cmptbl[16];
unsigned char codetbl[256];
unsigned char quickcodetbl[256];
unsigned char quicklentbl[256];
bitsleft = -16; /* init bit stream */
bits = 0;
SQgetbits(v,ZERO);
SQgetbits(type,16);
if (type&0x8000) /* 4 byte size field */
{
/* (skip nothing for 0x30fb) */
if (type&0x100) /* skip ulen */
{
SQgetbits(v,16);
SQgetbits(v,16);
}
type &= ~0x100;
SQgetbits(v,16); /* unpack len */
SQgetbits(ulen,16);
ulen |= (v<<16);
}
else
{
/* (skip nothing for 0x30fb) */
if (type&0x100) /* skip ulen */
{
SQgetbits(v,8);
SQgetbits(v,16);
}
type &= ~0x100;
SQgetbits(v,8); /* unpack len */
SQgetbits(ulen,16);
ulen |= (v<<16);
}
{
{
int numchars;
{
unsigned int basecmp;
{
unsigned int t;
SQgetbits(t,8); /* clue byte */
clue = (unsigned char)t;
}
numchars = 0;
numbits = 1;
basecmp = (unsigned int) 0;
/* decode bitnums */
do
{
basecmp <<= 1;
deltatbl[numbits] = basecmp-numchars;
SQgetnum(bitnum); /* # of codes of n bits */
bitnumtbl[numbits] = bitnum;
numchars += bitnum;
basecmp += bitnum;
cmp = 0;
if (bitnum) /* left justify cmp */
cmp = (basecmp << (16-numbits) & 0xffff);
cmptbl[numbits++] = cmp;
}
while (!bitnum || cmp); /* n+1 bits in cmp? */
}
cmptbl[numbits-1] = 0xffffffff; /* force match on most bits */
mostbits = numbits-1;
/* decode leapfrog code table */
{
signed char leap[256];
unsigned char nextchar;
SQmemset(leap,0,256);
nextchar = (unsigned char) -1;
for (i=0;i<numchars;++i)
{
int leapdelta=0;
SQgetnum(leapdelta);
++leapdelta;
do
{
++nextchar;
if (!leap[nextchar])
--leapdelta;
} while (leapdelta);
leap[nextchar] = 1;
codetbl[i] = nextchar;
}
}
}
/****************************************************************/
/* Make fast 8 tables */
/****************************************************************/
SQmemset(quicklentbl,64,256);
{
int bits;
int bitnum;
int numbitentries;
int nextcode;
int nextlen;
int i;
unsigned char *codeptr;
unsigned char *quickcodeptr;
unsigned char *quicklenptr;
codeptr = codetbl;
quickcodeptr = quickcodetbl;
quicklenptr = quicklentbl;
for (bits=1; bits<=mostbits; ++bits)
{
bitnum = bitnumtbl[bits];
if (bits>=9)
break;
numbitentries = 1<<(8-bits);
while (bitnum--)
{
nextcode = *codeptr++;
nextlen = bits;
if (nextcode==clue)
{
cluelen = bits;
nextlen = 96; /* will force out of main loop */
}
for (i=0; i<numbitentries; ++i)
{
*quickcodeptr++ = (unsigned char) nextcode;
*quicklenptr++ = (unsigned char) nextlen;
}
}
}
}
}
/****************************************************************/
/* Main decoder */
/****************************************************************/
for (;;)
{
unsigned char *quickcodeptr = quickcodetbl;
unsigned char *quicklenptr = quicklentbl;
goto nextloop;
/* quick 8 fetch */
do
{
*qd++ = quickcodeptr[bits>>24];
GET16BITS();
bits = bitsunshifted<<(16-bitsleft);
/* quick 8 decode */
nextloop:
numbits = quicklenptr[bits>>24];
bitsleft -= numbits;
if (bitsleft>=0)
{
do
{
*qd++ = quickcodeptr[bits>>24];
bits <<= numbits;
numbits = quicklenptr[bits>>24];
bitsleft -= numbits;
if (bitsleft<0) break;
*qd++ = quickcodeptr[bits>>24];
bits <<= numbits;
numbits = quicklenptr[bits>>24];
bitsleft -= numbits;
if (bitsleft<0) break;
*qd++ = quickcodeptr[bits>>24];
bits <<= numbits;
numbits = quicklenptr[bits>>24];
bitsleft -= numbits;
if (bitsleft<0) break;
*qd++ = quickcodeptr[bits>>24];
bits <<= numbits;
numbits = quicklenptr[bits>>24];
bitsleft -= numbits;
} while (bitsleft>=0);
}
bitsleft += 16;
} while (bitsleft>=0); /* would fetching 16 bits do it? */
bitsleft = bitsleft-16+numbits; /* back to normal */
/****************************************************************/
/* 16 bit decoder */
/****************************************************************/
{
unsigned char code;
if (numbits!=96)
{
cmp = (unsigned int) (bits>>16); /* 16 bit left justified compare */
numbits = 8;
do
{
++numbits;
}
while (cmp>=cmptbl[numbits]);
}
else
numbits = cluelen;
cmp = bits >> (32-(numbits));
bits <<= (numbits);
bitsleft -= (numbits);
code = codetbl[cmp-deltatbl[numbits]]; /* the code */
if (code!=clue && bitsleft>=0)
{
*qd++ = code;
goto nextloop;
}
if (bitsleft<0)
{
GET16BITS();
bits = bitsunshifted<<-bitsleft;
bitsleft += 16;
}
if (code!=clue)
{
*qd++ = code;
goto nextloop;
}
/* handle clue */
{
int runlen=0;
unsigned char *d=qd;
unsigned char *dest;
SQgetnum(runlen);
if (runlen) /* runlength sequence */
{
dest = d+runlen;
code = *(d-1);
do
{
*d++ = code;
} while (d<dest);
qd = d;
goto nextloop;
}
}
SQgetbits(v,1); /* End Of File */
if (v)
break;
{
unsigned int t;
SQgetbits(t,8); /* explicite byte */
code = (unsigned char)t;
}
*qd++ = code;
goto nextloop;
}
}
/****************************************************************/
/* Undelta */
/****************************************************************/
{
int i;
int nextchar;
if (type==0x32fb || type==0xb2fb) /* deltaed? */
{
i = 0;
qd = unpackbuf;
while (qd<unpackbuf+ulen)
{
i += (int) *qd;
*qd++ = (unsigned char) i;
}
}
else if (type==0x34fb || type==0xb4fb) /* accelerated? */
{
i = 0;
nextchar = 0;
qd = unpackbuf;
while (qd<unpackbuf+ulen)
{
i += (int) *qd;
nextchar += i;
*qd++ = (unsigned char) nextchar;
}
}
}
}
}
return(ulen);
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
/****************************************************************/
/* Information Functions */
/****************************************************************/
/* check for reasonable header: */
/* 30fb..35fb header */
bool GCALL HUFF_is(const void *compresseddata)
{
bool ok=false;
int packtype=ggetm(compresseddata,2);
if (packtype==0x30fb
|| packtype==0x31fb
|| packtype==0x32fb
|| packtype==0x33fb
|| packtype==0x34fb
|| packtype==0x35fb
|| packtype==0xb0fb
|| packtype==0xb1fb
|| packtype==0xb2fb
|| packtype==0xb3fb
|| packtype==0xb4fb
|| packtype==0xb5fb)
ok = true;
return(ok);
}
/****************************************************************/
/* Decode Functions */
/****************************************************************/
int GCALL HUFF_size(const void *compresseddata)
{
int len=0;
int packtype=ggetm(compresseddata,2);
int ssize=(packtype&0x8000)?4:3;
if (packtype&0x100) /* 31fb 33fb 35fb */
{
len = ggetm((char *)compresseddata+2+ssize,ssize);
}
else /* 30fb 32fb 34fb */
{
len = ggetm((char *)compresseddata+2,ssize);
}
return(len);
}
int GCALL HUFF_decode(void *dest, const void *compresseddata, int *compressedsize)
{
return(HUFF_decompress((unsigned char *)compresseddata, (unsigned char *)dest));
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,105 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Copyright (C) Electronic Arts Canada Inc. 1995-2002. All rights reserved.
/* ABSTRACT */
/*------------------------------------------------------------------*/
/* */
/* RefPack - Backward Reference Codex */
/* */
/* by FrANK G. Barchard, EAC */
/* */
/*------------------------------------------------------------------*/
/* */
/* Version Date SE History: */
/* ------- ---- -- -------- */
/* 0.10 941010 FB First codex prototype module */
/* 0.90 941019 FB #includable */
/* 1.00 950108 FB frozen API at 1.00 */
/* 1.01 010426 FB 32 size field */
/* */
/*------------------------------------------------------------------*/
/* */
/* Module Notes: */
/* ------------- */
/* used hash table and link table for speed */
/* Reentrant */
/* Files: refread.c refwrite.c refcodex.h (refmatch.asm pc) */
/* */
/*------------------------------------------------------------------*/
/* */
/* Format Notes: */
/* ------------- */
/* refpack is a sliding window (131k) lzss method, with byte */
/* oriented coding. */
/* */
/* huff fb6 style header: */
/* *10fb fb6 refpack 1.0 reference pack */
/* *90fb fb6 refpack 1.01 32 bit reference pack */
/* */
/* */
/* header: */
/* [10fb] [totalunpacksize] [nextunpacksize] */
/* 2 3 3 */
/* [90fb] [totalunpacksize] [nextunpacksize] */
/* 2 4 4 */
/* */
/* */
/* */
/* format is: */
/* ---------- */
/* 0ffnnndd_ffffffff short ref, f=0..1023,n=3..10,d=0..3 */
/* 10nnnnnn_ddffffff_ffffffff int ref, f=0..16384,n=4..67,d=0..3 */
/* 110fnndd_f.._f.._nnnnnnnn very int,f=0..131071,n=5..1028,d=0..3*/
/* 111ddddd literal, d=4..112 */
/* 111111dd eof, d=0..3 */
/* */
/*------------------------------------------------------------------*/
/* END ABSTRACT */
#include <string.h>
#include "codex.h"
#include "refcodex.h"
/****************************************************************/
/* Information Functions */
/****************************************************************/
CODEXABOUT *GCALL REF_about(void)
{
CODEXABOUT *info;
info = (CODEXABOUT *) galloc(sizeof(CODEXABOUT));
if (info)
{
memset(info, 0, sizeof(CODEXABOUT));
info->signature = QMAKEID(0,'R','E','F');
info->size = sizeof(CODEXABOUT);
info->version = 200; /* codex version number (200) */
info->decode = 1; /* supports decoding */
info->encode = 1; /* supports encoding */
info->size32 = 1; /* supports 32 bit size field */
strcpy(info->versionstr, "1.01"); /* version # */
strcpy(info->shorttypestr, "ref"); /* type */
strcpy(info->longtypestr, "Refpack"); /* longtype */
}
return(info);
}

View file

@ -0,0 +1,74 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Copyright (C) Electronic Arts Canada Inc. 1995-2002. All rights reserved. */
#ifndef __REFCODEX
#define __REFCODEX 1
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __CODEX_H
#error "Include codex.h before refcodex.h"
#endif
/****************************************************************/
/* REF Codex */
/****************************************************************/
/* Information Functions */
CODEXABOUT *GCALL REF_about(void);
bool GCALL REF_is(const void *compresseddata);
/* Decode Functions */
int GCALL REF_size(const void *compresseddata);
#ifdef __cplusplus
int GCALL REF_decode(void *dest, const void *compresseddata, int *compressedsize=0);
#else
int GCALL REF_decode(void *dest, const void *compresseddata, int *compressedsize);
#endif
/* Encode Functions */
#ifdef __cplusplus
int GCALL REF_encode(void *compresseddata, const void *source, int sourcesize, int *opts=0);
#else
int GCALL REF_encode(void *compresseddata, const void *source, int sourcesize, int *opts);
#endif
/****************************************************************/
/* Internal */
/****************************************************************/
#ifndef qmin
#define qmin(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef qmax
#define qmax(a,b) ((a)>(b)?(a):(b))
#endif
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,186 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Copyright (C) Electronic Arts Canada Inc. 1995-2002. All rights reserved.
#ifndef __REFREAD
#define __REFREAD 1
#include <string.h>
#include "codex.h"
#include "refcodex.h"
/****************************************************************/
/* Information Functions */
/****************************************************************/
/* check for reasonable header: */
/* 10fb header */
bool GCALL REF_is(const void *compresseddata)
{
bool ok=false;
int packtype=ggetm(compresseddata,2);
if (packtype==0x10fb
|| packtype==0x11fb
|| packtype==0x90fb
|| packtype==0x91fb)
ok = true;
return(ok);
}
/****************************************************************/
/* Decode Functions */
/****************************************************************/
int GCALL REF_size(const void *compresseddata)
{
int len=0;
int packtype=ggetm(compresseddata,2);
int ssize=(packtype&0x8000)?4:3;
if (packtype&0x100) /* 11fb */
{
len = ggetm((char *)compresseddata+2+ssize,ssize);
}
else /* 10fb */
{
len = ggetm((char *)compresseddata+2,ssize);
}
return(len);
}
int GCALL REF_decode(void *dest, const void *compresseddata, int *compressedsize)
{
unsigned char *s;
unsigned char *ref;
unsigned char *d;
unsigned char first;
unsigned char second;
unsigned char third;
unsigned char forth;
unsigned int run;
unsigned int type;
int ulen;
s = (unsigned char *) compresseddata;
d = (unsigned char *) dest;
ulen = 0L;
if (s)
{
type = *s++;
type = (type<<8) + *s++;
if (type&0x8000) /* 4 byte size field */
{
if (type&0x100) /* skip ulen */
s += 4;
ulen = *s++;
ulen = (ulen<<8) + *s++;
ulen = (ulen<<8) + *s++;
ulen = (ulen<<8) + *s++;
}
else
{
if (type&0x100) /* skip ulen */
s += 3;
ulen = *s++;
ulen = (ulen<<8) + *s++;
ulen = (ulen<<8) + *s++;
}
for (;;)
{
first = *s++;
if (!(first&0x80)) /* short form */
{
second = *s++;
run = first&3;
while (run--)
*d++ = *s++;
ref = d-1 - (((first&0x60)<<3) + second);
run = ((first&0x1c)>>2)+3-1;
do
{
*d++ = *ref++;
} while (run--);
continue;
}
if (!(first&0x40)) /* int form */
{
second = *s++;
third = *s++;
run = second>>6;
while (run--)
*d++ = *s++;
ref = d-1 - (((second&0x3f)<<8) + third);
run = (first&0x3f)+4-1;
do
{
*d++ = *ref++;
} while (run--);
continue;
}
if (!(first&0x20)) /* very int form */
{
second = *s++;
third = *s++;
forth = *s++;
run = first&3;
while (run--)
*d++ = *s++;
ref = d-1 - (((first&0x10)>>4<<16) + (second<<8) + third);
run = ((first&0x0c)>>2<<8) + forth + 5-1;
do
{
*d++ = *ref++;
} while (run--);
continue;
}
run = ((first&0x1f)<<2)+4; /* literal */
if (run<=112)
{
while (run--)
*d++ = *s++;
continue;
}
run = first&3; /* eof (+0..3 literal) */
while (run--)
*d++ = *s++;
break;
}
}
if (compressedsize)
*compressedsize = (int)((char *)s-(char *)compresseddata);
return(ulen);
}
#endif

View file

@ -0,0 +1,264 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Copyright (C) Electronic Arts Canada Inc. 1995-2002. All rights reserved.
#ifndef __REFWRITE
#define __REFWRITE 1
#include <string.h>
#include "codex.h"
#include "refcodex.h"
/****************************************************************/
/* Internal Functions */
/****************************************************************/
static unsigned int matchlen(unsigned char *s,unsigned char *d, unsigned int maxmatch)
{
unsigned int current;
for (current=0; current<maxmatch && *s++==*d++; ++current)
;
return(current);
}
#define HASH(cptr) (int)((((unsigned int)(unsigned char)cptr[0]<<8) | ((unsigned int)(unsigned char)cptr[2])) ^ ((unsigned int)(unsigned char)cptr[1]<<4))
static int refcompress(unsigned char *from, int len, unsigned char *dest, int maxback, int quick)
{
unsigned int tlen;
unsigned int tcost;
// unsigned int ccost; // context cost
unsigned int run;
unsigned int toffset;
unsigned int boffset;
unsigned int blen;
unsigned int bcost;
unsigned int mlen;
unsigned char *tptr;
unsigned char *cptr;
unsigned char *to;
unsigned char *rptr;
int countliterals=0;
int countshort=0;
int countint=0;
int countvint=0;
int hash;
int hoffset;
int minhoffset;
int i;
int *link;
int *hashtbl;
to = dest;
run = 0;
cptr = rptr = from;
if ((unsigned int)maxback > (unsigned int)131071)
maxback = 131071;
hashtbl = (int *) galloc(65536L*sizeof(int));
if (!hashtbl)
return(0);
link = (int *) galloc(131072L*sizeof(int));
if (!link)
return(0);
memset(hashtbl,-1,65536L*sizeof(int));
len -= 4;
while (len>=0)
{
boffset = 0;
blen = 2;
bcost = 2;
// ccost = 0;
mlen = qmin(len,1028);
tptr=cptr-1;
hash = HASH(cptr);
hoffset = hashtbl[hash];
minhoffset = qmax(cptr-from-131071,0);
if (hoffset>=minhoffset)
{
do
{
tptr = from+hoffset;
if (cptr[blen]==tptr[blen])
{
tlen = matchlen(cptr,tptr,mlen);
if (tlen > blen)
{
toffset = (cptr-1)-tptr;
if (toffset<1024 && tlen<=10) /* two byte int form */
tcost = 2;
else if (toffset<16384 && tlen<=67) /* three byte int form */
tcost = 3;
else /* four byte very int form */
tcost = 4;
if (tlen-tcost+4 > blen-bcost+4)
{
blen = tlen;
bcost = tcost;
boffset = toffset;
if (blen>=1028) break;
}
}
}
} while ((hoffset = link[hoffset&131071]) >= minhoffset);
}
// ccost = 0;
// if ((run<4) && ((run+blen)>=4))
// ccost = 1; // extra packet cost to switch out of literal into reference
// if (bcost>blen || (blen<=2 && bcost==blen && !ccost) || (len<4))
if (bcost>=blen || len<4)
{
hoffset = (cptr-from);
link[hoffset&131071] = hashtbl[hash];
hashtbl[hash] = hoffset;
++run;
++cptr;
--len;
}
else
{
while (run>3) /* literal block of data */
{
tlen = qmin(112,run&~3);
run -= tlen;
*to++ = (unsigned char) (0xe0+(tlen>>2)-1);
memcpy(to,rptr,tlen);
rptr += tlen;
to += tlen;
++countliterals;
}
if (bcost==2) /* two byte int form */
{
*to++ = (unsigned char) (((boffset>>8)<<5) + ((blen-3)<<2) + run);
*to++ = (unsigned char) boffset;
++countshort;
}
else if (bcost==3) /* three byte int form */
{
*to++ = (unsigned char) (0x80 + (blen-4));
*to++ = (unsigned char) ((run<<6) + (boffset>>8));
*to++ = (unsigned char) boffset;
++countint;
}
else /* four byte very int form */
{
*to++ = (unsigned char) (0xc0 + ((boffset>>16)<<4) + (((blen-5)>>8)<<2) + run);
*to++ = (unsigned char) (boffset>>8);
*to++ = (unsigned char) (boffset);
*to++ = (unsigned char) (blen-5);
++countvint;
}
if (run)
{
memcpy(to, rptr, run);
to += run;
run = 0;
}
if (quick)
{
hoffset = (cptr-from);
link[hoffset&131071] = hashtbl[hash];
hashtbl[hash] = hoffset;
cptr += blen;
}
else
{
for (i=0; i < (int)blen; ++i)
{
hash = HASH(cptr);
hoffset = (cptr-from);
link[hoffset&131071] = hashtbl[hash];
hashtbl[hash] = hoffset;
++cptr;
}
}
rptr = cptr;
len -= blen;
}
}
len += 4;
run += len;
while (run>3) /* no match at end, use literal */
{
tlen = qmin(112,run&~3);
run -= tlen;
*to++ = (unsigned char) (0xe0+(tlen>>2)-1);
memcpy(to,rptr,tlen);
rptr += tlen;
to += tlen;
}
*to++ = (unsigned char) (0xfc+run); /* end of stream command + 0..3 literal */
if (run)
{
memcpy(to,rptr,run);
to += run;
}
gfree(link);
gfree(hashtbl);
return(to-dest);
}
/****************************************************************/
/* Encode Function */
/****************************************************************/
int GCALL REF_encode(void *compresseddata, const void *source, int sourcesize, int *opts)
{
int maxback=131072;
int quick=0;
int plen;
int hlen;
/* simple fb6 header */
if (sourcesize>0xffffff) // 32 bit header required
{
gputm(compresseddata, (unsigned int) 0x90fb, 2);
gputm((char *)compresseddata+2, (unsigned int) sourcesize, 4);
hlen = 6L;
}
else
{
gputm(compresseddata, (unsigned int) 0x10fb, 2);
gputm((char *)compresseddata+2, (unsigned int) sourcesize, 3);
hlen = 5L;
}
plen = hlen+refcompress((unsigned char *)source, sourcesize, (unsigned char *)compresseddata+hlen, maxback, quick);
return(plen);
}
#endif

View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -0,0 +1,297 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// compress.c
// Compress interface for packets and files
// Author: Jeff Brown, January 1999
#include <stdio.h>
#include <stdlib.h>
#include "Lib/basetype.h"
#include "Noxcompress.h"
#include "CompLibHeader/lzhl.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma message("************************************** WARNING, optimization disabled for debugging purposes")
#endif
#define BLOCKSIZE 500000
#define NoxRead fread
#define DbgMalloc malloc
#define DbgFree free
#define DEBUG_LOG(x) {}
Bool DecompressFile (char *infile, char *outfile)
{
UnsignedInt rawSize = 0, compressedSize = 0;
FILE *inFilePtr = NULL;
FILE *outFilePtr= NULL;
char *inBlock = NULL;
char *outBlock = NULL;
LZHL_DHANDLE decompress;
Int ok = 0;
UnsignedInt srcSz, dstSz;
// Parameter checking
if (( infile == NULL ) || ( outfile == NULL ))
return FALSE;
inFilePtr = fopen( infile, "rb" );
if ( inFilePtr )
{
// Allocate the appropriate amount of memory
// Get compressed size of file.
fseek( inFilePtr, 0, SEEK_END );
compressedSize = ftell( inFilePtr );
fseek( inFilePtr, 0, SEEK_SET );
compressedSize -= sizeof(UnsignedInt);
// Get uncompressed size. Don't worry about endian,
// this is always INTEL baby!
NoxRead(&rawSize, 1, sizeof(UnsignedInt), inFilePtr);
// This is ick, but allocate a BIIIIG chunk o' memory x 2
inBlock = (char *) DbgMalloc( compressedSize );
outBlock= (char *) DbgMalloc( rawSize );
if (( inBlock == NULL ) || ( outBlock == NULL ))
return FALSE;
// Read in a big chunk o file
NoxRead(inBlock, 1, compressedSize, inFilePtr);
fclose(inFilePtr);
// Decompress
srcSz = compressedSize;
dstSz = rawSize;
// Just Do it!
decompress = LZHLCreateDecompressor();
for (;;)
{
ok = LZHLDecompress( decompress, outBlock + rawSize - dstSz, &dstSz,
inBlock + compressedSize - srcSz, &srcSz);
if ( !ok )
break;
if (srcSz <= 0)
break;
}
DEBUG_LOG(("Decompressed %s to %s, output size = %d\n", infile, outfile, rawSize));
LZHLDestroyDecompressor(decompress);
outFilePtr = fopen(outfile, "wb");
if (outFilePtr)
{
fwrite (outBlock, rawSize, 1, outFilePtr);
fclose(outFilePtr);
}
else
return FALSE;
// Clean up this mess
DbgFree(inBlock);
DbgFree(outBlock);
return TRUE;
} // End of if fileptr
return FALSE;
}
Bool CompressFile (char *infile, char *outfile)
{
UnsignedInt rawSize = 0;
UnsignedInt compressedSize = 0, compressed = 0, i = 0;
FILE *inFilePtr = NULL;
FILE *outFilePtr= NULL;
char *inBlock = NULL;
char *outBlock = NULL;
LZHL_CHANDLE compressor;
UnsignedInt blocklen;
// Parameter checking
if (( infile == NULL ) || ( outfile == NULL ))
return FALSE;
// Allocate the appropriate amount of memory
inFilePtr = fopen( infile, "rb" );
if ( inFilePtr )
{
// Get size of file.
fseek( inFilePtr, 0, SEEK_END );
rawSize = ftell( inFilePtr );
fseek( inFilePtr, 0, SEEK_SET );
// This is ick, but allocate a BIIIIG chunk o' memory x 2
inBlock = (char *) DbgMalloc(rawSize);
outBlock= (char *) DbgMalloc( LZHLCompressorCalcMaxBuf( rawSize ));
if (( inBlock == NULL ) || ( outBlock == NULL ))
return FALSE;
// Read in a big chunk o file
NoxRead(inBlock, 1, rawSize, inFilePtr);
fclose(inFilePtr);
// Compress
compressor = LZHLCreateCompressor();
for ( i = 0; i < rawSize; i += BLOCKSIZE )
{
blocklen = min((UnsignedInt)BLOCKSIZE, rawSize - i);
compressed = LZHLCompress(compressor, outBlock + compressedSize, inBlock + i, blocklen);
compressedSize += compressed;
}
LZHLDestroyCompressor(compressor);
outFilePtr = fopen(outfile, "wb");
if (outFilePtr)
{
// write out the uncompressed size first.
fwrite(&rawSize, sizeof(UnsignedInt), 1, outFilePtr);
fwrite(outBlock, compressedSize, 1, outFilePtr);
fclose(outFilePtr);
}
else
return FALSE;
// Clean up
DbgFree(inBlock);
DbgFree(outBlock);
return TRUE;
}
return FALSE;
}
Bool CompressPacket (char *inPacket, char *outPacket)
{
// Parameter checking
if (( inPacket == NULL ) || ( outPacket == NULL ))
return FALSE;
return TRUE;
}
Bool DecompressPacket (char *inPacket, char *outPacket)
{
// Parameter checking
if (( inPacket == NULL ) || ( outPacket == NULL ))
return FALSE;
return TRUE;
}
UnsignedInt CalcNewSize (UnsignedInt rawSize)
{
return LZHLCompressorCalcMaxBuf(rawSize);
}
Bool DecompressMemory (void *inBufferVoid, Int inSize, void *outBufferVoid, Int& outSize)
{
UnsignedByte *inBuffer = (UnsignedByte *)inBufferVoid;
UnsignedByte *outBuffer = (UnsignedByte *)outBufferVoid;
UnsignedInt rawSize = 0, compressedSize = 0;
LZHL_DHANDLE decompress;
Int ok = 0;
UnsignedInt srcSz, dstSz;
// Parameter checking
if (( inBuffer == NULL ) || ( outBuffer == NULL ) || ( inSize < 4 ) || ( outSize == 0 ))
return FALSE;
// Get compressed size of file.
compressedSize = inSize;
// Get uncompressed size.
rawSize = outSize;
// Decompress
srcSz = compressedSize;
dstSz = rawSize;
// Just Do it!
decompress = LZHLCreateDecompressor();
for (;;)
{
ok = LZHLDecompress( decompress, outBuffer + rawSize - dstSz, &dstSz,
inBuffer + compressedSize - srcSz, &srcSz);
if ( !ok )
break;
if (srcSz <= 0)
break;
}
LZHLDestroyDecompressor(decompress);
outSize = rawSize;
return TRUE;
}
Bool CompressMemory (void *inBufferVoid, Int inSize, void *outBufferVoid, Int& outSize)
{
UnsignedByte *inBuffer = (UnsignedByte *)inBufferVoid;
UnsignedByte *outBuffer = (UnsignedByte *)outBufferVoid;
UnsignedInt rawSize = 0;
UnsignedInt compressedSize = 0, compressed = 0, i = 0;
LZHL_CHANDLE compressor;
UnsignedInt blocklen;
// Parameter checking
if (( inBuffer == NULL ) || ( outBuffer == NULL ) || ( inSize < 4 ) || ( outSize == 0 ))
return FALSE;
rawSize = inSize;
// Compress
compressor = LZHLCreateCompressor();
for ( i = 0; i < rawSize; i += BLOCKSIZE )
{
blocklen = min((UnsignedInt)BLOCKSIZE, rawSize - i);
compressed = LZHLCompress(compressor, outBuffer + compressedSize, inBuffer + i, blocklen);
compressedSize += compressed;
}
LZHLDestroyCompressor(compressor);
outSize = compressedSize;
return TRUE;
}

View file

@ -0,0 +1,40 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// compress.h
// Compress/Decompression header.
// Author: Jeff Brown, January 1999
#ifndef __compress_h
#define __compress_h
#define MAP_EXTENSION ".map"
#define LZH_EXTENSION ".nxz"
#define RUL_EXTENSION ".rul"
Bool DecompressFile (char *infile, char *outfile);
Bool CompressFile (char *infile, char *outfile);
Bool CompressPacket (char *inPacket, char *outPacket);
Bool DecompressPacket (char *inPacket, char *outPacket);
UnsignedInt CalcNewSize (UnsignedInt rawSize);
Bool DecompressMemory (void *inBufferVoid, Int inSize, void *outBufferVoid, Int& outSize);
Bool CompressMemory (void *inBufferVoid, Int inSize, void *outBufferVoid, Int& outSize);
#endif __compress_h

View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -0,0 +1,30 @@
// BrowserDispatch.idl : IDL source for BrowserDispatch.dll
//
// This file will be processed by the MIDL tool to
// produce the type library (BrowserDispatch.tlb) and marshalling code.
import "oaidl.idl";
import "ocidl.idl";
[
uuid(C92D8250-A628-4CE5-823F-1A1F116EFCC9),
version(1.0),
helpstring("BrowserDispatch 1.0 Type Library")
]
library BROWSERDISPATCHLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[
object,
uuid(BC834510-C5BC-4B90-8C9A-0E4B1998796F),
dual,
helpstring("IBrowserDispatch Interface"),
pointer_default(unique)
]
interface IBrowserDispatch : IUnknown
{
[id(1), helpstring("method TestMethod")] HRESULT TestMethod(Int num1);
};
};

View file

@ -0,0 +1,192 @@
# Microsoft Developer Studio Project File - Name="EABrowserDispatch" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Static Library" 0x0104
CFG=EABrowserDispatch - Win32 Release
!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 "EABrowserDispatch.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 "EABrowserDispatch.mak" CFG="EABrowserDispatch - Win32 Release"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "EABrowserDispatch - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "EABrowserDispatch - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE "EABrowserDispatch - Win32 Internal" (based on "Win32 (x86) Static Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "EABrowserDispatch"
# PROP Scc_LocalPath "."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "EABrowserDispatch - 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 Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /G6 /MD /W3 /WX /GX /O2 /Ob2 /I "..\..\include" /D "_LIB" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_RELEASE" /YX /FD /c
# SUBTRACT CPP /Fr
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"..\..\Lib\EABrowserDispatch.lib"
!ELSEIF "$(CFG)" == "EABrowserDispatch - 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 Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD CPP /nologo /G6 /MDd /W3 /WX /Gm /GX /ZI /Od /I "..\..\include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# SUBTRACT CPP /Fr
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"..\..\Lib\EABrowserDispatchD.lib"
!ELSEIF "$(CFG)" == "EABrowserDispatch - Win32 Internal"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Internal"
# PROP BASE Intermediate_Dir "Internal"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Internal"
# PROP Intermediate_Dir "Internal"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /G6 /MD /W3 /WX /GX /O2 /I "..\..\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "_INTERNAL" /YX /FD /c
# SUBTRACT CPP /Fr
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"..\..\Lib\EABrowserDispatchI.lib"
!ENDIF
# Begin Target
# Name "EABrowserDispatch - Win32 Release"
# Name "EABrowserDispatch - Win32 Debug"
# Name "EABrowserDispatch - Win32 Internal"
# Begin Group "Source"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\BrowserDispatch.idl
!IF "$(CFG)" == "EABrowserDispatch - Win32 Release"
# PROP Ignore_Default_Tool 1
# Begin Custom Build - Running MIDL on $(InputPath)
InputPath=.\BrowserDispatch.idl
BuildCmds= \
midl.exe /out ..\..\Include\EABrowserDispatch /tlb "BrowserDispatch.tlb" /h "BrowserDispatch.h" /iid "BrowserDispatch_i.c" /mktyplib203 /win32 $(InputPath)
"..\..\Include\EABrowserDispatch\BrowserDispatch.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
"..\..\Include\EABrowserDispatch\BrowserDispatch_i.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
"..\..\Include\EABrowserDispatch\BrowserDispatch.tlb" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
# End Custom Build
!ELSEIF "$(CFG)" == "EABrowserDispatch - Win32 Debug"
# PROP Ignore_Default_Tool 1
# Begin Custom Build - Running MIDL on $(InputPath)
InputPath=.\BrowserDispatch.idl
BuildCmds= \
midl.exe /out ..\..\Include\EABrowserDispatch /tlb "BrowserDispatch.tlb" /h "BrowserDispatch.h" /iid "BrowserDispatch_i.c" /mktyplib203 /win32 $(InputPath)
"..\..\Include\EABrowserDispatch\BrowserDispatch.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
"..\..\Include\EABrowserDispatch\BrowserDispatch_i.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
"..\..\Include\EABrowserDispatch\BrowserDispatch.tlb" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
# End Custom Build
!ELSEIF "$(CFG)" == "EABrowserDispatch - Win32 Internal"
# PROP Ignore_Default_Tool 1
# Begin Custom Build - Running MIDL on $(InputPath)
InputPath=.\BrowserDispatch.idl
BuildCmds= \
midl.exe /out ..\..\Include\EABrowserDispatch /tlb "BrowserDispatch.tlb" /h "BrowserDispatch.h" /iid "BrowserDispatch_i.c" /mktyplib203 /win32 $(InputPath)
"..\..\Include\EABrowserDispatch\BrowserDispatch.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
"..\..\Include\EABrowserDispatch\BrowserDispatch_i.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
"..\..\Include\EABrowserDispatch\BrowserDispatch.tlb" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
# End Custom Build
!ENDIF
# End Source File
# Begin Source File
SOURCE=..\..\Include\EABrowserDispatch\BrowserDispatch_i.c
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=..\..\Include\EABrowserDispatch\BrowserDispatch.h
# End Source File
# End Group
# End Target
# End Project

View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -0,0 +1,150 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Dune Emperor **
** **
** Module: <module> (<prefix>_) **
** **
** Version: $ID$ **
** **
** File name: audassrt.cpp **
** **
** Created by: 12/??/00 TR **
** **
** Description: <description> **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <string.h>
#include <stdarg.h>
#include <assert.h>
//#include <windows.h>
#include <stdio.h>
#include <time.h>
#include <stdarg.h>
#include <wpaudio/altypes.h>
/*****************************************************************************
** Externals **
*****************************************************************************/
extern void WindowsDebugPrint( const char * lpOutputString );
/*****************************************************************************
** Defines **
*****************************************************************************/
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
char *DBG_type_struct_is_dead = "Invalid structure";
char assert_msg_buf[10*1024];
static FILE *err_file = NULL;
static int total_errors;
static char _msg_buf[sizeof(assert_msg_buf)*2];
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
#ifdef _DEBUG
/******************************************************************/
/* */
/* */
/******************************************************************/
void __cdecl _assert_printf ( const char *format, ...)
{
va_list args;
va_start( args, format ); /* Initialize variable arguments. */
vsprintf ( assert_msg_buf, format, args );
va_end( args );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void __cdecl _aud_debug_printf ( const char *format, ...)
{
va_list args;
va_start( args, format ); /* Initialize variable arguments. */
vsprintf ( _msg_buf, format, args );
va_end( args );
WindowsDebugPrint ( _msg_buf );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void _aud_assert ( const char *, const char *file, int line, const char *reason )
{
sprintf ( _msg_buf, "%s(%d) : Error : ASSERT - %s\n", file, line, reason );
WindowsDebugPrint ( _msg_buf );
_assert ( _msg_buf, (void *) file, line );
}
#endif
/*****************************************************************************
** Public Functions **
*****************************************************************************/

View file

@ -0,0 +1,230 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Dune Emperor **
** **
** Module: <module> (<prefix>_) **
** **
** Version: $ID$ **
** **
** File name: audattr.cpp **
** **
** Created by: 11/16/95 TR **
** **
** Description: <description> **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <wpaudio/altypes.h>
#include <wpaudio/level.h>
#include <wpaudio/attributes.h>
DBG_DECLARE_TYPE ( AudioAttribs )
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/*****************************************************************************
** Public Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioAttribsInit ( AudioAttribs *attr )
{
DBG_ASSERT ( attr != NULL);
DBG_SET_TYPE ( attr, AudioAttribs );
AudioLevelInit ( &attr->VolumeLevel, AUDIO_VOLUME_MAX );
AudioLevelInit ( &attr->PitchLevel, 100);
AudioLevelInit ( &attr->PanPosition, AUDIO_PAN_CENTER);
AudioAttribsSetPitchDuration (attr, SECONDS(1), 10 );
AudioAttribsSetVolumeDuration (attr, SECONDS(1), AUDIO_LEVEL_MAX );
AudioAttribsSetPanDuration (attr, SECONDS(1), AUDIO_LEVEL_MAX );
AudioLevelSet ( &attr->VolumeLevel, AUDIO_VOLUME_MAX );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioAttribsUpdate ( AudioAttribs *attr )
{
DBG_ASSERT_TYPE ( attr, AudioAttribs );
AudioLevelUpdate ( &attr->VolumeLevel );
AudioLevelUpdate ( &attr->PitchLevel );
AudioLevelUpdate ( &attr->PanPosition );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioAttribsChanged ( AudioAttribs *attr )
{
DBG_ASSERT_TYPE ( attr, AudioAttribs );
return AudioLevelChanged ( &attr->VolumeLevel ) || AudioLevelChanged ( &attr->PitchLevel ) || AudioLevelChanged ( &attr->PanPosition );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioAttribsApply ( AudioAttribs *attr, AudioAttribs *mod )
{
DBG_ASSERT_TYPE ( attr, AudioAttribs );
DBG_ASSERT_TYPE ( mod, AudioAttribs );
AudioLevelSet ( &attr->VolumeLevel, AudioLevelApply ( &mod->VolumeLevel, AudioAttribsGetVolume ( attr ) ));
AudioLevelUpdate ( &attr->VolumeLevel );
{
// apply pitch
int level;
int change;
level = AudioAttribsGetPitch ( attr );
change = AudioAttribsGetPitch ( mod );
level = (level * change) / 100;
AudioAttribsSetPitch ( attr, level );
AudioLevelUpdate ( &attr->PitchLevel );
}
{
// apply pan
int pos;
if ( (pos = AudioAttribsGetPan ( mod ) - AUDIO_PAN_CENTER) != 0 )
{
if ( ( pos = pos + AudioAttribsGetPan ( attr )) < AUDIO_PAN_LEFT )
{
pos = AUDIO_PAN_LEFT ;
}
else if ( pos > AUDIO_PAN_RIGHT )
{
pos = AUDIO_PAN_RIGHT ;
}
AudioAttribsSetPan ( attr, pos );
}
AudioLevelUpdate ( &attr->PanPosition );
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioAttribsUsed ( AudioAttribs *attr )
{
AudioLevelUsed ( &attr->VolumeLevel );
AudioLevelUsed ( &attr->PanPosition );
AudioLevelUsed ( &attr->PitchLevel );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioAttribsCalcPitch ( AudioAttribs *attr, int pitch )
{
int level;
DBG_ASSERT_TYPE ( attr, AudioAttribs );
level = AudioAttribsGetPitch ( attr );
return ( pitch * level ) / 100;
}

View file

@ -0,0 +1,586 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Dune Emperor **
** **
** Module: <module> (<prefix>_) **
** **
** Version: $ID$ **
** **
** File name: audcache.cpp **
** **
** Created by: 04/30/99 TR **
** **
** Description: <description> **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <string.h>
#include <memory.h>
#include <wpaudio/altypes.h> // always include this header first
#include <wpaudio/memory.h>
#include <wpaudio/list.h>
#include <wpaudio/source.h>
#include <wpaudio/cache.h>
#include <wpaudio/profiler.h>
#include <wsys/File.h>
// 'assignment within condition expression'.
#pragma warning(disable : 4706)
DBG_DECLARE_TYPE ( AudioCache );
DBG_DECLARE_TYPE ( AudioCacheItem );
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
#define DEBUG_CACHE 0
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
static void audioCacheAssetClose ( AudioCache *cache )
{
if ( cache->assetFile )
{
cache->assetFile->close();
cache->assetFile = NULL;
}
cache->assetBytesLeft = 0;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioCacheAssetOpen ( AudioCache *cache, const char *name )
{
audioCacheAssetClose ( cache );
if ( name == NULL || cache->openAssetCB == NULL )
{
return FALSE;
}
cache->assetFile = cache->openAssetCB( name );
if ( !cache->assetFile )
{
return FALSE;
}
if ( !AudioFormatReadWaveFile ( cache->assetFile, &cache->assetFormat, &cache->assetBytesLeft ))
{
audioCacheAssetClose ( cache );
return FALSE;
}
return TRUE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int audioCacheAssetRead ( AudioCache *cache, void *data, int bytes )
{
if ( bytes > cache->assetBytesLeft )
{
bytes = cache->assetBytesLeft;
}
return cache->assetFile ? cache->assetFile->read ( data, bytes ) : 0 ;
}
/*****************************************************************************
** Public Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
AudioCache* AudioCacheCreate ( int cacheSize, int maxItems, int frameSize )
{
AudioCache *cache;
int frameBytes;
int pages;
ALLOC_STRUCT ( cache, AudioCache );
DBG_SET_TYPE ( cache, AudioCache );
cache->frameSize = frameSize;
frameBytes = sizeof ( AudioFrame ) + frameSize;
pages = cacheSize/frameBytes;
cache->framePool = MemoryPoolCreate ( pages, frameBytes );
cache->itemPool = MemoryPoolCreate ( maxItems, sizeof ( AudioCacheItem ) );
ListInit ( &cache->items );
cache->assetFile = NULL;
AudioFormatInit ( &cache->assetFormat );
ProfCacheInit ( &cache->profile, pages, frameSize );
ProfCacheUpdateInterval ( &cache->profile, 10 ); // every ten milliseconds
if ( !cache->framePool || !cache->itemPool )
{
AudioCacheDestroy ( cache );
cache = NULL;
}
return cache;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioCacheDestroy ( AudioCache *cache )
{
AudioCacheItem *item;
DBG_ASSERT_TYPE ( cache, AudioCache );
while ( ( item = (AudioCacheItem *) ListNodeNext ( &cache->items )) )
{
AudioCacheItemFree ( item );
}
if ( cache->framePool )
{
MemoryPoolDestroy ( cache->framePool );
}
if ( cache->itemPool )
{
MemoryPoolDestroy ( cache->itemPool );
}
DBG_INVALIDATE_TYPE ( cache );
AudioMemFree ( cache );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
AudioCacheItem* AudioCacheGetItem ( AudioCache *cache, const char *name )
{
AudioCacheItem *item, *head;
DBG_ASSERT_TYPE ( cache, AudioCache );
item = head = (AudioCacheItem *) &cache->items ;
while ( (item = (AudioCacheItem *) item->nd.next ) != head )
{
if ( item->valid && item->name == name )
{
return item;
}
}
return NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioCacheInvalidate ( AudioCache *cache )
{
AudioCacheItem *item, *head;
DBG_ASSERT_TYPE ( cache, AudioCache );
item = head = (AudioCacheItem *) &cache->items ;
while ( (item = (AudioCacheItem *) item->nd.next ) != head )
{
item->valid = FALSE;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
AudioCacheOpenCB* AudioCacheSetOpenCB ( AudioCache *cache, AudioCacheOpenCB *cb )
{
DBG_ASSERT_TYPE ( cache, AudioCache );
AudioCacheOpenCB *old = cache->openAssetCB;
cache->openAssetCB = cb;
return old;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
AudioCacheItem* AudioCacheLoadItem ( AudioCache *cache, const char *name )
{
AudioCacheItem *item;
int error;
DBG_ASSERT_TYPE ( cache, AudioCache );
if ( (item = AudioCacheGetItem ( cache, name )))
{
#if DEBUG_CACHE
DBGPRINTF (("ACACHE: %10s - Cached\n", item->name ));
#endif
ListNodeRemove ( &item->nd );
ListNodeAppend ( &cache->items, &item->nd );
ProfCacheHit ( &cache->profile );
return item;
}
ProfCacheMiss ( &cache->profile );
// item is not in the cache so load it
// see first if the sample exists
#if DEBUG_CACHE
DBGPRINTF (("ACACHE: %10s - Loading\n", name ));
#endif
ProfCacheLoadStart ( &cache->profile, 0 );
if ( !audioCacheAssetOpen( cache, name ))
{
#if DEBUG_CACHE
DBGPRINTF (("does not exist\n"));
#endif
goto none;
}
item = (AudioCacheItem *) MemoryPoolGetItem ( cache->itemPool );
if ( !item )
{
// free the oldest item so that we can use it's item struct
AudioCacheFreeOldestItem ( cache );
item = (AudioCacheItem *) MemoryPoolGetItem ( cache->itemPool );
if ( !item )
{
// the oldest item could not be freed because it was still playing
DBGPRINTF (("Audio cache overflow\n"));
audioCacheAssetClose ( cache );
goto none;
}
}
// prepare item for use
item->name = name;
item->cache = cache;
ListNodeInit ( &item->nd );
LockInit ( &item->lock );
AudioSampleInit ( &item->sample );
AudioSampleSetName ( &item->sample, name );
AudioFormatInit ( &item->format );
item->sample.Format = &item->format;
DBG_SET_TYPE ( item, AudioCacheItem );
error = FALSE;
// ok load sample data in to cache
{
int bytesToTransfer;
int bytes;
int bytesTransfered;
bytesToTransfer = cache->assetBytesLeft;
while ( bytesToTransfer )
{
AudioFrame *frame;
void *data;
if ( (bytes = cache->frameSize ) > bytesToTransfer )
{
bytes = bytesToTransfer;
}
while ( ! (frame = (AudioFrame *) MemoryPoolGetItem ( cache->framePool )))
{
if ( !AudioCacheFreeOldestItem ( cache ) )
{
break;
}
}
if ( !frame )
{
error = TRUE;
break;
}
data = (void *) ( (uint) frame + sizeof ( AudioFrame ));
AudioFrameInit ( frame, data, bytes );
AudioSampleAddFrame ( &item->sample, frame );
bytesTransfered = audioCacheAssetRead ( cache, data, bytes );
ProfCacheAddLoadBytes ( &cache->profile, bytesTransfered );
ProfCacheAddPage ( &cache->profile );
ProfCacheFill ( &cache->profile, bytesTransfered );
if ( bytesTransfered != bytes )
{
error = TRUE;
break;
}
bytesToTransfer -= bytesTransfered;
}
}
if ( error )
{
#if DEBUG_CACHE
DBGPRINTF (("FAILED\n"));
#endif
AudioCacheItemFree ( item );
goto none;
}
#if DEBUG_CACHE
DBGPRINTF (("done\n"));
#endif
// update the format structure
memcpy ( &item->format, &cache->assetFormat, sizeof ( AudioFormat) );
ListNodeAppend ( &cache->items, &item->nd );
item->valid = TRUE;
audioCacheAssetClose ( cache );
ProfCacheLoadEnd ( &cache->profile );
return item;
none:
ProfCacheLoadEnd ( &cache->profile );
return NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// AudioCacheFreeOldestItem()
// Free the oldest UNUSED item in the list. These should tend to be
// towards the end of the list, as old events should expire and unlock.
// But not always (e.g. loops), so don't count on it.
int AudioCacheFreeOldestItem( AudioCache *cache )
{
AudioCacheItem *item;
DBG_ASSERT_TYPE( cache, AudioCache );
item = (AudioCacheItem*) ListNodePrev( &cache->items );
while ( item )
{
if ( !AudioCacheItemInUse( item ) )
{
AudioCacheItemFree( item );
return TRUE;
}
item = (AudioCacheItem*) ListNodePrev( (ListNode*) item );
}
return FALSE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioCacheItemLock ( AudioCacheItem *item )
{
DBG_ASSERT_TYPE ( item, AudioCacheItem );
LockAcquire ( &item->lock );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioCacheItemUnlock ( AudioCacheItem *item )
{
DBG_ASSERT_TYPE ( item, AudioCacheItem );
LockRelease ( &item->lock );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioCacheItemInUse ( AudioCacheItem *item )
{
DBG_ASSERT_TYPE ( item, AudioCacheItem );
return Locked ( &item->lock );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioCacheItemFree ( AudioCacheItem *item )
{
DBG_ASSERT_TYPE ( item, AudioCacheItem );
if ( ListNodeInList ( &item->nd ))
{
ListNodeRemove ( &item->nd );
}
DBG_MSGASSERT ( !AudioCacheItemInUse ( item ), ("cache item is still in use"));
#if DEBUG_CACHE
DBGPRINTF (("ACACHE: %10s - Freeing\n", AudioBagGetItemName ( item->cache->bag, item->id )));
#endif
// return frames to frame pool
{
AudioFrame *frame;
while ( (frame = (AudioFrame *) ListNodeNext ( &item->sample.Frames )) )
{
ListNodeRemove ( &frame->nd );
ProfCacheRemove ( &item->cache->profile, frame->Bytes );
AudioFrameDeinit ( frame );
ProfCacheRemovePage ( &item->cache->profile );
MemoryPoolReturnItem ( item->cache->framePool, frame );
}
}
AudioSampleDeinit ( &item->sample );
DBG_INVALIDATE_TYPE ( item );
MemoryPoolReturnItem ( item->cache->itemPool, item );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
AudioSample* AudioCacheItemSample ( AudioCacheItem *item )
{
DBG_ASSERT_TYPE ( item, AudioCacheItem );
return &item->sample;
}

View file

@ -0,0 +1,710 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Dune Emperor **
** **
** Module: <module> (<prefix>_) **
** **
** Version: $ID$ **
** **
** File name: audchanl.cpp **
** **
** Created by: 11/16/96 TR **
** **
** Description: <description> **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <string.h>
#include <wpaudio/altypes.h>
#include <wpaudio/memory.h>
#include <wpaudio/list.h>
#include <wpaudio/channel.h>
#include <wpaudio/device.h>
// 'assignment within condition expression'.
#pragma warning(disable : 4706)
DBG_DECLARE_TYPE ( AudioChannel)
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
static int audioChannelSampleDone ( AudioChannel *chan );
static int audioChannelNextSample ( AudioChannel *chan );
static int audioChannelNextFrame ( AudioChannel *chan );
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
AudioChannel* audioChannelCreate ( AudioDevice *dev )
{
AudioChannel *chan = NULL;
DBG_ASSERT_TYPE ( dev , AudioDevice );
ALLOC_STRUCT ( chan, AudioChannel );
DBG_SET_TYPE ( chan, AudioChannel );
ListNodeInit ( &chan->nd );
AudioControlInit ( &chan->Control );
AudioAttribsInit ( &chan->attribs );
audioChannelMakeStandard ( chan ); // set up for standard processing
chan->Device = dev;
chan->driver = dev->driver;
chan->current_format = dev->DefaultFormat;
chan->format_changed = FALSE;
chan->drv_format_changed = FALSE;
#ifndef IG_FINAL_RELEASE
chan->sample_name[0] = 0;
#endif
if ( dev->driver->openChannel ( chan ) != vNO_ERROR )
{
DBGPRINTF (("Audio driver failed to open a new channel\n"));
goto error;
}
return chan;
error:
if ( chan )
{
audioChannelDestroy ( chan );
}
return NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void audioChannelDestroy ( AudioChannel *chan )
{
DBG_ASSERT_TYPE ( chan, AudioChannel );
chan->driver->closeChannel ( chan );
AudioMemFree ( chan );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void audioChannelMakeStandard ( AudioChannel *chan )
{
DBG_ASSERT_TYPE ( chan, AudioChannel );
// reset callbacks
chan->drvCBNextFrame = (AudioChannelCB *) audioChannelNextFrame;
chan->drvCBNextSample = (AudioChannelCB *) audioChannelNextSample;
chan->drvCBSampleDone = (AudioChannelCB *) audioChannelSampleDone;
chan->CB_NextFrame = NULL;
chan->CB_NextSample = NULL;
chan->CB_SampleDone = NULL;
chan->CB_Stop = NULL;
chan->Data = NULL;
chan->Type = AUDIO_CHANNEL_TYPE_STD;
// reset control
AudioControlInit ( &chan->Control );
chan->Control.Priority = AUD_NORMAL_PRIORITY;
// reset attribs
chan->GroupAttribs = audioStdChannelAttribs;
chan->SfxAttribs = NULL;
chan->CompAttribs = NULL;
chan->FadeAttribs = NULL;
AudioAttribsInit ( &chan->ChannelAttribs );
// reset sequencer
// clear audio
chan->sample = NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void audioChannelRecalcAttribs ( AudioChannel *chan )
{
AudioDevice *dev;
DBG_ASSERT_TYPE ( chan, AudioChannel );
dev = (AudioDevice *) chan->Device;
DBG_ASSERT_TYPE ( dev, AudioDevice );
AudioAttribsInit ( &chan->attribs );
AudioAttribsApply ( &chan->attribs, &chan->ChannelAttribs );
AudioAttribsUsed ( &chan->ChannelAttribs );
if ( chan->SfxAttribs )
{
AudioAttribsApply ( &chan->attribs, chan->SfxAttribs );
AudioAttribsUsed ( chan->SfxAttribs );
}
if ( chan->GroupAttribs )
{
AudioAttribsApply ( &chan->attribs, chan->GroupAttribs );
}
if ( chan->CompAttribs )
{
AudioAttribsApply ( &chan->attribs, chan->CompAttribs );
}
if ( chan->FadeAttribs )
{
AudioAttribsApply ( &chan->attribs, chan->FadeAttribs );
}
AudioAttribsApply ( &chan->attribs, &dev->Attribs );
if ( dev->GroupAttribs )
{
AudioAttribsApply ( &chan->attribs, dev->GroupAttribs );
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioChannelNextFrame ( AudioChannel *chan )
{
int err;
DBG_ASSERT ( chan != NULL );
if ( chan->CB_NextFrame )
{
// user is controlling frame access
if ( (err = chan->CB_NextFrame ( chan )) != vNO_ERROR )
{
// there was an error
DBGPRINTF(("Frame error:\n"));
chan->bytesInFrame = 0;
chan->bytesRemaining = 0;
chan->frameData = NULL;
return err;
};
DBG_ASSERT ( chan->bytesInFrame >= 0 );
DBG_ASSERT ( chan->bytesRemaining >= 0 );
return vNO_ERROR;
}
// user is not controlling frame access so we handle the default behaviour
DBG_ASSERT ( chan->bytesRemaining >= 0 ); // something corrupted this field
if ( chan->frame )
{
if ( ( chan->frame = (AudioFrame*) ListNodeNext ( &chan->frame->nd )) )
{
// there is more data to be got
chan->frameData = (char *) chan->frame->Data;
chan->bytesInFrame = chan->frame->Bytes;
chan->bytesRemaining = chan->bytesInFrame;
DBG_MSGASSERT ( chan->frame->sample == chan->sample, ("frame does not belong to sample") );
return vNO_ERROR;
}
}
// data has been exhausted
chan->bytesInFrame = 0; // no more frames, drvCBNextSample() will be called
return vNO_ERROR;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioChannelNextSample ( AudioChannel *chan )
{
DBG_ASSERT ( chan != NULL );
if (chan->Control.LoopCount)
{
if ( chan->Control.LoopCount != AUDIO_CTRL_LOOP_FOREVER )
{
chan->Control.LoopCount--;
}
AudioChannelSetSample ( chan, chan->sample );
}
else
{
AudioChannelSetSample ( chan, NULL );
}
if ( chan->CB_NextSample)
{
chan->CB_NextSample ( chan );
}
if ( chan->sample )
{
chan->driver->queueIt ( chan );
}
return vNO_ERROR;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioChannelSampleDone ( AudioChannel *chan )
{
// reset channel back to default
chan->sample = NULL;
FLAGS_CLEAR ( chan->Control.Status, mAUDIO_CTRL_PLAYING|mAUDIO_CTRL_PAUSED );
chan->Control.LoopCount = 0;
AudioAttribsInit ( &chan->ChannelAttribs );
#ifndef IG_FINAL_RELEASE
chan->sample_name[0] = 0;
#endif
if ( chan->CB_SampleDone )
{
return chan->CB_SampleDone ( chan );
}
return vNO_ERROR;
}
/*****************************************************************************
** Public Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioChannelTaken ( AudioChannel *chan )
{
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
return chan->Control.Status & mAUDIO_CTRL_ALLOCATED;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioChannelReserve ( AudioChannel *chan, AudioChannelType type )
{
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
DBG_ASSERT ( chan->Type == AUDIO_CHANNEL_TYPE_STD ); // you can only reserve standard channels
chan->Type = type;
AudioChannelStop ( chan );
FLAGS_SET ( chan->Control.Status, mAUDIO_CTRL_ALLOCATED );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioChannelRelease ( AudioChannel *chan )
{
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
if ( chan->Control.Status & mAUDIO_CTRL_ALLOCATED)
{
FLAGS_CLEAR ( chan->Control.Status, mAUDIO_CTRL_ALLOCATED );
chan->Type = AUDIO_CHANNEL_TYPE_STD;
audioChannelMakeStandard ( chan );
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioChannelDestroy ( AudioChannel *chan)
{
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
AudioChannelStop ( chan ); // stop anything that is playing
audioRemoveChannel ( chan );
audioChannelDestroy ( chan );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioChannelStop ( AudioChannel *chan )
{
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
chan->driver->lock ( chan);
if ( chan->Control.Status & (mAUDIO_CTRL_PLAYING|mAUDIO_CTRL_PAUSED) )
{
chan->driver->stop ( chan );
DBG_ASSERT ( !(chan->Control.Status & (mAUDIO_CTRL_PLAYING|mAUDIO_CTRL_PAUSED)) );
}
chan->driver->unlock ( chan );
if ( chan->CB_Stop )
{
chan->CB_Stop ( chan );
}
chan->sample = NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioChannelPause ( AudioChannel *chan )
{
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
chan->driver->lock ( chan );
if ( chan->Control.Status & mAUDIO_CTRL_PLAYING )
{
FLAGS_CLEAR (chan->Control.Status, mAUDIO_CTRL_PLAYING);
chan->driver->pause ( chan );
FLAGS_SET (chan->Control.Status, mAUDIO_CTRL_PAUSED);
}
chan->driver->unlock ( chan );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioChannelResume ( AudioChannel *chan )
{
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
chan->driver->lock ( chan );
if ( chan->Control.Status & mAUDIO_CTRL_PAUSED )
{
chan->driver->resume ( chan );
FLAGS_CLEAR (chan->Control.Status, mAUDIO_CTRL_PAUSED);
FLAGS_SET (chan->Control.Status, mAUDIO_CTRL_PLAYING);
}
chan->driver->unlock ( chan );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioChannelLock ( AudioChannel *chan )
{
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
chan->driver->lock ( chan );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioChannelUnlock ( AudioChannel *chan )
{
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
chan->driver->unlock ( chan );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioChannelUse ( AudioChannel *chan )
{
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
chan->driver->lock ( chan );
FLAGS_SET ( chan->Control.Status, mAUDIO_CTRL_INUSE);
chan->driver->unlock ( chan );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioChannelNoUse ( AudioChannel *chan )
{
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
chan->driver->lock ( chan );
FLAGS_CLEAR ( chan->Control.Status, mAUDIO_CTRL_INUSE);
chan->driver->unlock ( chan );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioChannelStart ( AudioChannel *chan )
{
int err = ERROR_CODE_FAIL;
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
if ( (chan->Control.Status & (mAUDIO_CTRL_PAUSED | mAUDIO_CTRL_PLAYING)))
{
DBGPRINTF (("Failed to start channel as channel is currently active\n"));
return ERROR_CODE_NO_CHANNEL;
}
if ( chan->sample == NULL )
{
DBGPRINTF (("Failed to start channel as no sample data was given\n"));
return ERROR_CODE_MISSING_DATA;
}
AudioAttribsUpdate ( &chan->ChannelAttribs );
audioChannelRecalcAttribs ( chan );
chan->driver->lock ( chan );
if ( (err = chan->driver->start ( chan )) == vNO_ERROR )
{
FLAGS_SET ( chan->Control.Status, mAUDIO_CTRL_PLAYING);
}
chan->driver->unlock ( chan );
return err;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioChannelSetSample ( AudioChannel *chan, AudioSample *sample )
{
DBG_ASSERT ( audioInitialized ); // AudioSetUp() was not called
DBG_ASSERT_TYPE ( chan, AudioChannel );
if ( (chan->sample = sample) )
{
DBG_ASSERT_TYPE ( sample, AudioSample);
#ifndef IG_FINAL_RELEASE
strncpy ( chan->sample_name, sample->name, sizeof(chan->sample_name));
chan->sample_name[ sizeof(chan->sample_name) -1 ] = 0;
#endif
if ( chan->frame = AudioSampleFirstFrame ( sample ) )
{
chan->frameData = (char *) chan->frame->Data;
chan->bytesInFrame = chan->frame->Bytes;
chan->bytesRemaining = chan->bytesInFrame;
sample->Data = NULL;
DBG_MSGASSERT ( chan->frame->sample == sample, ("frame does not belong to sample") );
}
else
{
chan->frameData = chan->sample->Data;
chan->bytesInFrame = chan->sample->Bytes;
chan->bytesRemaining = chan->bytesInFrame;
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioChannelSetFormat ( AudioChannel *chan, AudioFormat *new_format )
{
if ( !memcmp ( new_format, &chan->current_format, sizeof ( AudioFormat )))
{
chan->format_changed = FALSE;
}
else
{
chan->format_changed = TRUE;
chan->current_format = *new_format;
}
return chan->format_changed;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioChannelIsAudible ( AudioChannel *chan )
{
return (chan->Control.Status & mAUDIO_CTRL_PLAYING);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioControlInit ( AudioControl *ctrl )
{
ctrl->LoopCount = 0;
ctrl->Status = 0;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,288 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Dune Emperor **
** **
** Module: <module> (<prefix>_) **
** **
** Version: $ID$ **
** **
** File name: audlevel.cpp **
** **
** Created by: 11/16/95 TR **
** **
** Description: <description> **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <wpaudio/altypes.h>
#include <wpaudio/level.h>
#include <wpaudio/time.h>
// 'assignment within condition expression'.
#pragma warning(disable : 4706)
DBG_DECLARE_TYPE ( AudioLevel )
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/*****************************************************************************
** Public Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioLevelInit ( AudioLevel *level, int startLevel )
{
DBG_ASSERT ( level != NULL );
DBG_SET_TYPE ( level, AudioLevel );
level->flags = 0;
level->lastTime = AudioGetTime ();
AudioLevelSetDuration ( level, SECONDS(1), AUDIO_LEVEL_MAX);
AudioLevelSet ( level, startLevel );
AudioLevelUpdate ( level );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioLevelSet ( AudioLevel *level, int newLevel )
{
DBG_ASSERT_TYPE ( level, AudioLevel );
DBG_ASSERT (newLevel>=AUDIO_LEVEL_MIN);
DBG_ASSERT (newLevel<=AUDIO_LEVEL_MAX);
level->flags |= AUDIO_LEVEL_SET;
level->newLevel = (newLevel<<AUDIO_LEVEL_SCALE);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioLevelForce( AudioLevel *level)
{
DBG_ASSERT_TYPE ( level, AudioLevel );
level->flags |= AUDIO_LEVEL_SET;
}
#ifdef _DEBUG
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioLevelApply ( AudioLevel *level, int val )
{
DBG_ASSERT_TYPE ( level, AudioLevel );
DBG_ASSERT (val >= AUDIO_LEVEL_MIN_VAL);
DBG_ASSERT (val <= AUDIO_LEVEL_MAX_VAL);
return AUDIO_LEVEL_APPLY(level,val);
}
#endif
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioLevelAdjust ( AudioLevel *level, int newLevel )
{
DBG_ASSERT_TYPE ( level, AudioLevel );
DBG_ASSERT (newLevel>=AUDIO_LEVEL_MIN);
DBG_ASSERT (newLevel<=AUDIO_LEVEL_MAX);
level->flags &= ~AUDIO_LEVEL_SET;
if ( level->newLevel == level->level)
{
level->lastTime = AudioGetTime ();
}
level->newLevel = newLevel<<AUDIO_LEVEL_SCALE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioLevelSetDuration ( AudioLevel *level, TimeStamp time, int range )
{
DBG_ASSERT_TYPE ( level, AudioLevel );
DBG_ASSERT ( time != 0 );
DBG_ASSERT (range > 0);
DBG_ASSERT (range <= AUDIO_LEVEL_MAX);
level->change = (range<< AUDIO_LEVEL_SCALE) / (uint) time;
level->duration = time;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioLevelUpdate ( AudioLevel *level )
{
int dif;
int delta; // amount to move by this update
TimeStamp time, thisTime;
DBG_ASSERT_TYPE ( level, AudioLevel );
if ( (dif = (level->newLevel - level->level)) )
{
if (level->flags & AUDIO_LEVEL_SET )
{
level->level = level->newLevel;
}
else
{
// calculate what the delta change is for this update
thisTime = AudioGetTime ( ) ;
time = thisTime - level->lastTime;
level->lastTime = thisTime; // remember time of this update
// the next check avoid overflowing the delta
if (time > level->duration)
{
time = level->duration;
}
delta = level->change * (uint) time;
if (dif<0)
{
if ( delta > (-dif))
{
level->level += dif;
}
else
{
level->level -= delta;
}
}
else
{
if ( delta > dif )
{
level->level += dif;
}
else
{
level->level += delta;
}
}
}
// there was a change in the level
level->flags |= AUDIO_LEVEL_CHANGED;
return TRUE;
}
// there has been no change this update
return FALSE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioLevelGetVal( AudioLevel *level )
{
return (level->newLevel>>AUDIO_LEVEL_SCALE);
}

View file

@ -0,0 +1,418 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Dune Emperor **
** **
** Module: <module> (<prefix>_) **
** **
** Version: $ID$ **
** **
** File name: audlists.cpp **
** **
** Created by: 04/01/95 TR **
** **
** Description: <description> **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <wpaudio/altypes.h>
#include <wpaudio/list.h>
// 'assignment within condition expression'.
#pragma warning(disable : 4706)
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/*****************************************************************************
** Public Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
void ListInit ( ListHead *head )
{
head->prev = head->next = head;
head->pri = (Priority) head; // this identifies the node as a head node
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ListNodeInit ( ListNode *node )
{
node->prev = node->next= node;
node->pri = 0;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int ListAddNodeSortAscending( ListHead *head, ListNode *new_node )
{
ListNode *node;
Priority pri;
int index;
index = 0;
pri = new_node->pri;
node = (ListNode*) head;
while( (node = ListNodeNext ( node )))
{
if ( pri <= node->pri )
{
ListNodeInsert ( node, new_node );
return index;
}
index++;
}
ListNodeInsert ( head, new_node );
return index;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ListAddNode( ListHead *head, ListNode *new_node )
{
ListNode *node;
Priority pri;
pri = new_node->pri;
node = (ListNode*) head;
while( (node = ListNodeNext ( node )))
{
if (node->pri <= pri )
{
ListNodeInsert ( node, new_node );
return;
}
}
ListNodeInsert ( head, new_node );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ListAddNodeAfter( ListHead *head, ListNode *new_node )
{
ListNode *node;
Priority pri;
pri = new_node->pri;
node = (ListNode*) head;
while( (node = ListNodeNext ( node )))
{
if (node->pri < pri )
{
ListNodeInsert ( node, new_node );
return;
}
}
ListNodeInsert ( head, new_node );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ListMerge( ListHead *from, ListHead *to )
{
ListNode *first,
*last,
*node;
first = from->next;
last = from->prev;
if ( first == (ListNode*) from )
{
/* the from list is empty so there is nothing to do */
return;
}
node = to->prev;
node->next = first;
first->prev = node;
last->next = (ListNode*) to;
to->prev = last;
ListInit ( from ); /* make the from list empty now */
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int ListCountItems ( ListHead *head )
{
ListNode *node;
int count = 0;
node = head->next;
while(node!=(ListNode*)head)
{
count++;
node = node->next;
}
return count;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
ListNode* ListFirstItem ( ListHead *head )
{
return ListNextItem ((ListNode*) head );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
ListNode* ListNextItem ( ListNode *node )
{
if ( !node )
{
return NULL;
}
return ( ListNodeNext ( node ));
}
/******************************************************************/
/* */
/* */
/******************************************************************/
ListNode* ListGetItem( ListHead *head, int number )
{
ListNode *node;
node = head->next;
while( node != (ListNode*) head )
{
if ( number-- == 0 )
{
return node;
}
node = node->next;
}
return NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ListNodeInsert( ListNode *node, ListNode *new_node )
{
new_node->prev = node->prev;
new_node->next = node;
node->prev = new_node;
new_node->prev->next = new_node;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ListNodeAppend( ListNode *node, ListNode *new_node )
{
new_node->prev = node;
new_node->next = node->next;
node->next = new_node;
new_node->next->prev = new_node;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ListNodeRemove( ListNode *node )
{
node->prev->next = node->next;
node->next->prev = node->prev;
node->prev = node->next = node; // so we know that the node is not in a list
}
/******************************************************************/
/* */
/* */
/******************************************************************/
ListNode* ListNodeNext( ListNode *node )
{
ListNode *next;
next = node->next;
if ( next && ListNodeIsHead ( next ))
{
return NULL;
}
return next;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
ListNode* ListNodePrev (ListNode *node)
{
ListNode *next;
next = node->prev;
if ( ListNodeIsHead ( next ))
{
return NULL;
}
return next;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
ListNode* ListNodeLoopNext (ListNode *node)
{
ListNode *next;
next = node->next;
if ( ListNodeIsHead ( next ))
{
// skip head node
next = next->next;
if ( ListNodeIsHead ( next ))
{
return NULL; // it is an empty list
}
}
return next;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
ListNode* ListNodeLoopPrev (ListNode *node)
{
ListNode *next;
next = node->prev;
if ( ListNodeIsHead ( next ))
{
// skip head node
next = next->prev;
if ( ListNodeIsHead ( next ))
{
return NULL; // it is an empty list
}
}
return next;
}

View file

@ -0,0 +1,158 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Dune Emperor **
** **
** Module: <module> (<prefix>_) **
** **
** Version: $ID$ **
** **
** File name: audlock.cpp **
** **
** Created by: 04/01/95 TR **
** **
** Description: <description> **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <wpaudio/altypes.h>
#include <wpaudio/lock.h>
DBG_DECLARE_TYPE ( Lock )
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/*****************************************************************************
** Public Functions **
*****************************************************************************/
#ifdef _DEBUG
/******************************************************************/
/* */
/* */
/******************************************************************/
void LockInit ( volatile Lock *lock )
{
DBG_ASSERT ( lock != NULL);
LOCK_INIT(lock);
DBG_SET_TYPE ( lock, Lock );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void LockAcquire ( volatile Lock *lock)
{
DBG_ASSERT_TYPE ( lock, Lock);
DBG_ASSERT ( lock->count >= 0 );
LOCK_ACQUIRE (lock);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void LockRelease ( volatile Lock *lock)
{
DBG_ASSERT_TYPE ( lock, Lock);
DBG_ASSERT ( lock->count > 0 );
LOCK_RELEASE(lock);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int Locked ( volatile Lock *lock)
{
DBG_ASSERT_TYPE ( lock, Lock);
DBG_ASSERT ( lock->count >= 0 );
return LOCKED(lock);
}
#endif

View file

@ -0,0 +1,277 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Dune Emperor **
** **
** Module: <module> (<prefix>_) **
** **
** Version: $ID$ **
** **
** File name: audmpool.cpp **
** **
** Created by: 10/23/95 TR **
** **
** Description: <description> **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <string.h>
#include <wpaudio/altypes.h>
#include <wpaudio/memory.h>
// 'assignment within condition expression'.
#pragma warning(disable : 4706)
DBG_DECLARE_TYPE ( AudioMemoryPool )
DBG_DECLARE_TYPE ( MemoryItem )
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/*****************************************************************************
** Public Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
AudioMemoryPool* MemoryPoolCreate( uint items, uint size )
{
uint poolsize;
AudioMemoryPool *pool;
MemoryItem *item;
uint i;
DBG_ASSERT ( items > 0 );
DBG_ASSERT ( size > 0 );
poolsize = items*(size + sizeof(MemoryItem)) + sizeof (AudioMemoryPool);
if ((pool = (AudioMemoryPool *) AudioMemAlloc ( poolsize )))
{
item = (MemoryItem *)( (uint)pool + (uint) sizeof(AudioMemoryPool));
pool->next = item;
DBG_CODE ( pool->itemsOut = 0;)
DBG_CODE ( pool->numItems = items;)
DBG_SET_TYPE ( pool, AudioMemoryPool );
for ( i=0; i < items-1; i++)
{
DBG_SET_TYPE ( item, MemoryItem );
item->next = (MemoryItem *) ( (uint) item + (uint) (sizeof(MemoryItem) + size) );
item = item->next;
}
item->next = NULL;
DBG_SET_TYPE ( item, MemoryItem );
}
return pool;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void MemoryPoolDestroy ( AudioMemoryPool *pool )
{
DBG_ASSERT_TYPE ( pool, AudioMemoryPool );
DBG_CODE
(
if ( pool->itemsOut > 0 )
{
DBGPRINTF ( ( "Destroying memory pool with %d items still out\n", pool->itemsOut) );
}
)
AudioMemFree ( pool );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void *MemoryPoolGetItem ( AudioMemoryPool *pool )
{
MemoryItem *item = NULL;
DBG_ASSERT_TYPE ( pool, AudioMemoryPool );
if (! (item = pool->next) )
{
return NULL;
}
DBG_CODE ( pool->itemsOut++;)
DBG_MSGASSERT ( pool->itemsOut <= pool->numItems,( "pool overflow" ));
DBG_ASSERT_TYPE ( item, MemoryItem ); // !!! Memory corruption !!!
pool->next = item->next;
return (void *) ( (uint) item + (uint) sizeof(MemoryItem));
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void MemoryPoolReturnItem ( AudioMemoryPool *pool, void *data )
{
MemoryItem *item;
DBG_ASSERT_TYPE ( pool, AudioMemoryPool );
DBG_ASSERT_PTR ( data );
item = (MemoryItem *) ( (uint) data - (uint) sizeof(MemoryItem));
DBG_ASSERT_TYPE ( item, MemoryItem ); // returning invalid item to pool
item->next = pool->next;
pool->next = item;
DBG_MSGASSERT ( pool->itemsOut > 0,( "Pool underflow" )); // returning more items than were taken
DBG_CODE ( pool->itemsOut--; )
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int MemoryPoolCount ( AudioMemoryPool *pool )
{
MemoryItem *item = NULL;
int count = 0;
DBG_ASSERT_TYPE ( pool, AudioMemoryPool );
if ( (item = pool->next) )
{
while ( item )
{
count++;
item = item->next;
}
}
return count;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioAddSlash ( char *string )
{
int len = strlen ( string );
if ( len )
{
if ( string[len-1] != '\\' )
{
string[len] = '\\';
string[len+1] = 0;
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioHasPath ( const char *string )
{
return ( strchr ( string, ':' ) || strchr ( string, '\\' ) || strchr ( string, '/' ) || strchr ( string, '.'));
}

View file

@ -0,0 +1,636 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Dune Emperor **
** **
** Module: <module> (<prefix>_) **
** **
** Version: $ID$ **
** **
** File name: audprof.cpp **
** **
** Created by: mm/dd/yy <author> **
** **
** Description: <description> **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <wpaudio/profiler.h>
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/*****************************************************************************
** Public Functions **
*****************************************************************************/
#ifndef IG_FINAL_RELEASE
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheInit ( ProfileData *prof, int pages, int pageSize )
{
memset ( prof, 0, sizeof ( ProfileData ));
if ( !QueryPerformanceFrequency( (LARGE_INTEGER*) &prof->freq ))
{
prof->freq = 0;
}
prof->update = prof->freq;
prof->cacheSize = pages*pageSize;
prof->numPages = pages;
prof->pageSize = pageSize;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheDeinit ( ProfileData * )
{
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheNewFrame ( ProfileData *prof )
{
int total;
prof->frames++;
if ( prof->totalTime > prof->longestFrame )
{
prof->nextLongestFrame = prof->longestFrame;
prof->nextLongestFramePages = prof->longestFramePages;
prof->nextLongestFrameBytes = prof->longestFrameBytes;
prof->longestFrame = prof->totalTime ;
prof->longestFrameBytes = prof->totalDataBytes;
prof->longestFramePages = prof->pageCount;
}
else if ( prof->totalTime > prof->nextLongestFrame )
{
prof->nextLongestFrame = prof->totalTime ;
prof->nextLongestFrameBytes = prof->totalDataBytes;
prof->nextLongestFramePages = prof->pageCount;
}
prof->pageCount = 0;
if ( (total = prof->hits + prof->misses ))
{
prof->HitPercent = (prof->hits *100) / total;
}
prof->BytesPerFrame = prof->totalFrameBytes / prof->frames;
if ( prof->frames > 3*30 )
{
prof->totalFrameBytes = 0;
prof->frames = 0;
}
if ( ! (prof->frames % 30) )
{
prof->hits = 0;
prof->misses = 0;
}
if ( prof->totalTime > prof->update )
{
int ms;
ms = (int) ( (prof->totalTime *1000 )/prof->freq);
prof->TotalBytesPerSecond = (int) (((unsigned __int64) prof->totalDataBytes *1000)/ ms);
prof->totalDataBytes = 0;
prof->totalTime = 0;
ms = (int) ((prof->totalDecompTime *1000 )/prof->freq) ;
if ( ms )
{
prof->DecompBytesPerSecond = (int) (((unsigned __int64) prof->totalDecompBytes *1000)/ ms);
}
else
{
prof->DecompBytesPerSecond = 0;
}
prof->totalDecompBytes = 0;
prof->totalDecompTime = 0;
ms = (int) ((prof->totalLoadTime *1000 )/prof->freq );
if ( ms )
{
prof->LoadBytesPerSecond = (int) (((unsigned __int64)prof->totalLoadBytes *1000)/ ms);
}
else
{
prof->LoadBytesPerSecond = 0;
}
prof->totalLoadBytes = 0;
prof->totalLoadTime = 0;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheLoadStart ( ProfileData *prof, int bytes )
{
QueryPerformanceCounter( (LARGE_INTEGER*)&prof->start );
prof->dbytes = bytes;
prof->start_decomp = 0;
prof->pageCount++;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheAddLoadBytes ( ProfileData *prof, int bytes )
{
prof->dbytes += bytes;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheDecompress ( ProfileData *prof, int bytes )
{
QueryPerformanceCounter( (LARGE_INTEGER*)&prof->start_decomp );
prof->lbytes = bytes;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheLoadEnd ( ProfileData *prof )
{
QueryPerformanceCounter( (LARGE_INTEGER*)&prof->end );
prof->totalTime += prof->end - prof->start;
prof->totalDataBytes += prof->dbytes;
prof->totalFrameBytes += prof->dbytes;
if ( prof->start_decomp )
{
prof->totalLoadTime += prof->start_decomp - prof->start;
prof->totalDecompTime += prof->end - prof->start_decomp;
prof->totalLoadBytes += prof->lbytes;
prof->totalDecompBytes += prof->dbytes;
}
else
{
prof->totalLoadTime += prof->end - prof->start;
prof->totalLoadBytes += prof->dbytes;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheMiss ( ProfileData *prof )
{
prof->misses++;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheHit ( ProfileData *prof )
{
prof->hits++;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheAddPage ( ProfileData *prof )
{
prof->pagesUsed++;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheRemovePage ( ProfileData *prof )
{
prof->pagesUsed--;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheFill ( ProfileData *prof, int bytes )
{
prof->cacheUsed += bytes;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheRemove ( ProfileData *prof, int bytes )
{
prof->cacheUsed -= bytes;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheText ( ProfileData *prof, void ( *print) ( char *text ) )
{
char buf[1024];
int used;
int filled;
print ("Audio Cache Stats\n");
sprintf( buf, "Hits: %d%%\n", prof->HitPercent );
print ( buf );
if ( prof->numPages )
{
used = (prof->pagesUsed *100)/prof->numPages;
}
else
{
used = 0;
}
if ( prof->pagesUsed * prof->pageSize )
{
filled = (prof->cacheUsed *100)/ (prof->pagesUsed * prof->pageSize );
}
else
{
filled = 0;
}
sprintf( buf, "Used: %d%% (%d%%)\n", used, filled );
print ( buf );
sprintf( buf, "KbPS: %d.%02d (%d.%02d,%d.%02d)\n",
prof->TotalBytesPerSecond/1024, ((prof->TotalBytesPerSecond%1024)*100)/1024,
prof->LoadBytesPerSecond/1024, ((prof->LoadBytesPerSecond%1024)*100)/1024,
prof->DecompBytesPerSecond/1024, ((prof->DecompBytesPerSecond%1024)*100)/1024);
print ( buf );
sprintf( buf, "KPF: %d.%02d\n",
prof->BytesPerFrame/1024, ((prof->BytesPerFrame%1024)*100)/1024 );
print ( buf );
sprintf( buf, " LF: %d.%02ds; %d pages; %d Kb\n",
(int) (prof->longestFrame/prof->freq), (int)(((prof->longestFrame % prof->freq)*100)/prof->freq),
prof->longestFramePages, prof->longestFrameBytes/1024 );
print ( buf );
sprintf( buf, "NLF: %d.%02ds; %d pages; %d Kb\n",
(int) (prof->nextLongestFrame/prof->freq), (int) (((prof->nextLongestFrame % prof->freq)*100)/prof->freq),
prof->nextLongestFramePages, prof->nextLongestFrameBytes/1024 );
print ( buf );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfCacheUpdateInterval ( ProfileData *prof, int mseconds )
{
prof->update = (prof->freq * mseconds )/ 1000;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static ProfStamp calc_ticks ( ProfStamp start, ProfStamp end )
{
if ( start < end )
{
return end - start;
}
return ((ProfStamp)0xffffffffffffffff) - start + end ;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static void calc_stats ( ProfileCPU &prof )
{
if ( prof.lastCPU )
{
prof.cpuUsage = (int) ((prof.lastTicks*((ProfStamp)1000))/prof.lastCPU);
}
else
{
prof.cpuUsage = 0;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfileCPUInit ( ProfileCPU &prof )
{
memset ( &prof, 0, sizeof ( ProfileCPU ));
if ( !QueryPerformanceFrequency( (LARGE_INTEGER*) &prof.freq ))
{
prof.freq = 0;
}
prof.updateInterval = SECONDS(1);
if ( prof.freq )
{
prof.overflowInterval = SECONDS ( (((ProfStamp) 0xffffffffffffffff) / prof.freq) );
if ( prof.overflowInterval < prof.updateInterval )
{
prof.updateInterval = prof.overflowInterval;
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfileCPUDeinit ( ProfileCPU &prof )
{
prof;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfileCPUStart ( ProfileCPU &prof )
{
if ( prof.state == PROF_STATE_IDLE )
{
QueryPerformanceCounter( (LARGE_INTEGER*)&prof.start );
if ( prof.lastStart )
{
prof.totalCPUTicks += calc_ticks ( prof.lastStart, prof.start );
}
prof.lastStart = prof.start;
prof.state = PROF_STATE_PROFILING;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfileCPUPause ( ProfileCPU &prof )
{
if ( prof.state == PROF_STATE_PROFILING )
{
ProfStamp end;
QueryPerformanceCounter( (LARGE_INTEGER*) &end );
prof.totalTicks += calc_ticks ( prof.start, end );
prof.state = PROF_STATE_PAUSED;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfileCPUResume ( ProfileCPU &prof )
{
if ( prof.state == PROF_STATE_PAUSED )
{
QueryPerformanceCounter( (LARGE_INTEGER*) &prof.start );
prof.state = PROF_STATE_PROFILING;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfileCPUEnd ( ProfileCPU &prof )
{
ProfStamp end;
QueryPerformanceCounter( (LARGE_INTEGER*) &end );
if ( prof.state != PROF_STATE_IDLE )
{
if ( prof.start && prof.totalCPUTicks )
{
prof.totalTicks += calc_ticks ( prof.start, end );
}
prof.state = PROF_STATE_IDLE;
}
TimeStamp now = AudioGetTime();
TimeStamp lastUpdate = now - prof.lastUpdate;
if ( lastUpdate > prof.updateInterval )
{
prof.lastUpdate = now;
if ( lastUpdate < prof.overflowInterval )
{
// we can use the data
prof.lastTicks = prof.totalTicks;
prof.lastCPU = prof.totalCPUTicks;
calc_stats ( prof );
}
prof.totalTicks = 0;
prof.totalCPUTicks = 0;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int ProfileCPUUsage ( ProfileCPU &prof )
{
return prof.cpuUsage;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int ProfileCPUTicks ( ProfileCPU &prof )
{
prof;
return 0;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfileCPUSetName ( ProfileCPU &prof, const char *name )
{
strncpy ( prof.name, name, MAX_PROF_NAME );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void ProfileCPUPrint ( ProfileCPU &prof, void ( *print) ( char *text ) )
{
char buffer[200];
if ( prof.freq )
{
sprintf ( buffer, "%s : CPU %3d.%1d / %I64d", prof.name, prof.cpuUsage/10, prof.cpuUsage%10, prof.lastTicks );
}
else
{
sprintf ( buffer, "%s : CPU (no timer)" );
}
print ( buffer );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
#endif

View file

@ -0,0 +1,632 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Dune Emperor **
** **
** Module: <module> (<prefix>_) **
** **
** Version: $ID$ **
** **
** File name: <filename> **
** **
** Created by: 04/05/96 TR **
** **
** Description: <description> **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <memory.h>
#include <stdio.h>
#include <string.h>
#include <wpaudio/altypes.h>
#include <wpaudio/memory.h>
#include <wpaudio/time.h>
#include <wpaudio/source.h>
#include <wsys/file.h>
//#include "asimp3\mss.h"
//#include "asimp3\mp3dec.h"
// 'assignment within condition expression'.
#pragma warning(disable : 4706)
DBG_DECLARE_TYPE ( AudioSample )
DBG_DECLARE_TYPE ( AudioFormat )
DBG_DECLARE_TYPE ( AudioFrame )
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
//
// Sample rate in samples/second for [MPEG25][MPEG version][value]
//
static const int sample_rate[2][2][4] =
{{
{ 22050L,24000L,16000L,22050L },
{ 44100L,48000L,32000L,44100L }
},
{
{ 11025L,12000L, 8000L,11025L },
{ 44100L,48000L,32000L,44100L }
}};
//
// Bit rate in bits/second for [MPEG version][value]
//
static const int bit_rate[2][15] =
{
{ 0L,8000L,16000L,24000L,32000L,40000L,48000L,56000L,64000L,80000L,96000L,112000L,128000L,144000L,160000L }
,
{ 0L,32000L,40000L,48000L,56000L,64000L,80000L,96000L,112000L,128000L,160000L,192000L,224000L,256000L,320000L }
};
/*****************************************************************************
** Public Data **
*****************************************************************************/
const short MSADPCM_StdCoef[7][2] = {
{ 256, 0},
{ 512,-256},
{ 0, 0},
{ 192, 64},
{ 240, 0},
{ 460,-208},
{ 392,-232}
};
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/*****************************************************************************
** Public Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
AudioSample* AudioCreateSample( uint bytes )
{
AudioSample *sample;
DBG_ASSERT ( bytes > 0 );
ALLOC_STRUCT ( sample, AudioSample );
AudioSampleInit ( sample );
sample->Bytes = bytes;
if ( ! ( sample->Data = ( char *) AudioMemAlloc ( sample->Bytes ) ))
{
goto error;
}
return sample;
error:
if ( sample )
{
AudioSampleDestroy ( sample );
}
return NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioSampleDestroy ( AudioSample *sample )
{
DBG_ASSERT_TYPE ( sample, AudioSample );
if ( sample->Data )
{
AudioMemFree ( sample->Data );
}
AudioMemFree ( sample );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioSampleInit ( AudioSample *sample )
{
DBG_ASSERT ( sample != NULL );
DBG_SET_TYPE ( sample, AudioSample );
sample->Data = NULL;
sample->Bytes = 0;
sample->Format = NULL;
sample->Attribs = NULL;
ListInit ( &sample->Frames );
#ifdef _DEBUG
sample->name[0] = 0;
#endif
}
#ifndef IG_FINAL_RELEASE
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioSampleSetName ( AudioSample *sample, const char *orig_name )
{
int diff;
char *buffer = NULL;
char *name;
DBG_ASSERT_TYPE ( sample, AudioSample );
buffer = (char *) AudioMemAlloc ( strlen ( orig_name ) + 1);
name = buffer;
sample->name[0] = 0;
if ( name )
{
char *ptr;
strcpy ( name, orig_name );
ptr = strrchr ( name, '.' );
if ( ptr )
{
*ptr = 0;
}
ptr = strrchr ( name, '\\' );
if ( ptr )
{
name = ptr + 1;
}
if ( ( diff = (strlen ( name ) - (sizeof(sample->name) - 1))) > 0 )
{
strcpy ( sample->name, "...");
name = name + diff + 3;
strcat ( sample->name, name );
}
else
{
strcpy ( sample->name, name );
}
}
if ( buffer )
{
AudioMemFree ( buffer );
}
}
#endif
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioSampleAddFrame ( AudioSample *sample, AudioFrame *frame )
{
DBG_ASSERT_TYPE ( sample, AudioSample );
DBG_ASSERT_TYPE ( frame, AudioFrame );
ListAddToTail ( &sample->Frames, &frame->nd );
sample->Bytes += frame->Bytes;
frame->sample = sample;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
AudioFrame* AudioSampleFirstFrame ( AudioSample *sample )
{
DBG_ASSERT_TYPE ( sample, AudioSample );
return (AudioFrame *) ListNodeNext ( &sample->Frames );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioSampleDeinit ( AudioSample *sample )
{
DBG_ASSERT_TYPE ( sample, AudioSample );
DBG_INVALIDATE_TYPE ( sample );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioFormatBytes ( AudioFormat *format, TimeStamp time )
{
DBG_ASSERT_TYPE ( format, AudioFormat );
return (int) ( ((TimeStamp)format->BytesPerSecond*time)/(TimeStamp)SECONDS(1));
}
/******************************************************************/
/* */
/* */
/******************************************************************/
TimeStamp AudioFormatTime ( AudioFormat *format, int bytes )
{
DBG_ASSERT_TYPE ( format, AudioFormat );
if ( format->BytesPerSecond )
{
return (((TimeStamp)bytes)*(TimeStamp)SECONDS(1))/(TimeStamp)format->BytesPerSecond;
}
return 0;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioFormatInit ( AudioFormat *format )
{
DBG_ASSERT ( format != NULL );
memset ( format, 0, sizeof ( AudioFormat));
DBG_SET_TYPE ( format, AudioFormat );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioFormatUpdate ( AudioFormat *format )
{
DBG_ASSERT_TYPE ( format, AudioFormat );
DBG_ASSERT ( format->Channels != 0 );
DBG_ASSERT ( format->SampleWidth >0 );
DBG_ASSERT ( format->Rate >0 );
DBG_ASSERT ( format->Compression < AUDIO_COMPRESS_MAX_ID );
if ( format->Compression != AUDIO_COMPRESS_MP3 )
{
format->BytesPerSecond = format->Channels*format->SampleWidth*format->Rate;
if ( format->Compression == AUDIO_COMPRESS_IMA_ADPCM
|| format->Compression == AUDIO_COMPRESS_MS_ADPCM )
{
format->BytesPerSecond >>= 2; // 4:1 compression
}
}
else
{
int mpeg1 = (W_BitsGet ( format->cdata.mp3.Header, 1, 19 ));
int bitrateindex = W_BitsGet ( format->cdata.mp3.Header, 4, 12 );
format->BytesPerSecond = bit_rate[mpeg1][bitrateindex] / 8;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioFrameInit ( AudioFrame *frame, void *data, int bytes )
{
DBG_ASSERT_PTR ( frame );
DBG_SET_TYPE ( frame, AudioFrame );
frame->Bytes = bytes;
frame->Data = data;
ListNodeInit ( &frame->nd );
frame->sample = NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioFrameDeinit ( AudioFrame *frame )
{
DBG_ASSERT_TYPE ( frame, AudioFrame );
frame->sample = NULL;
DBG_INVALIDATE_TYPE ( frame );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
#define MAX_SYNC_SEARCH (10*1024) // max bytes that will be searched for the sync
int AudioFormatReadMP3File ( File *file, AudioFormat *format, int *datasize )
{
unsigned char buffer[MAX_SYNC_SEARCH+3];
int bytes;
int pos;
unsigned int header = 0;
int bitrateindex = 0; // initialize to prevent compiler warning
int sampling_frequency = 0; // initialize to prevent compiler warning
int layer;
int data_start = file->position ();
bytes = file->read ( buffer, sizeof (buffer ));
pos = 0;
while ( !header && bytes >= 4 )
{
unsigned char mask = 0xFF;
// find next sync
while ( bytes >= 3 )
{
if ( (buffer[pos]&mask) == mask )
{
if ( mask == 0xE0 )
{
pos--;
bytes++;
//header = (buffer[pos] << 24) || (buffer[pos+1] << 16) || (buffer[pos+2] << 8) || buffer[pos+3];
header = MAKEID ( buffer[pos], buffer[pos+1], buffer[pos+2], buffer[pos+3]);
break;
}
mask = 0xE0;
}
else
{
mask = 0xFF;
}
pos++;
bytes--;
}
// validate the header
bitrateindex = W_BitsGet ( header, 4, 12 );
sampling_frequency = W_BitsGet ( header, 2, 10 );
layer = (W_BitsGet ( header, 2, 17 ));
if ( bitrateindex == 0x0f || sampling_frequency == 0x03 || layer != 1)
{
header = 0;
pos++;
bytes--;
continue;
}
}
if (!header )
{
return FALSE;
}
int mpeg25 = !(W_BitsGet ( header, 1, 20 ));
int mpeg1 = (W_BitsGet ( header, 1, 19 ));
int mode = (W_BitsGet ( header, 2, 6 ));
format->Compression = AUDIO_COMPRESS_MP3;
format->SampleWidth = 2;
format->Channels = (mode == 3) ? 1 : 2;
format->BytesPerSecond = bit_rate[mpeg1][bitrateindex] / 8;
format->Rate = sample_rate[mpeg25][mpeg1][sampling_frequency];
format->Flags = mAUDIO_FORMAT_PCM;
format->cdata.mp3.Header = header;
AudioFormatUpdate ( format );
if ( datasize )
{
*datasize = file->size() - (data_start + pos) ;
}
file->seek ( data_start+pos, File::START );
return TRUE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioFormatSeekToPos ( File *file, const AudioFormat *format, int pos, int data_start )
{
if ( pos )
{
int block_size = 0;
if ( format->Compression == AUDIO_COMPRESS_MP3 )
{
AudioFormat tformat;
file->seek ( pos+data_start, File::START );
AudioFormatInit ( &tformat );
int found = FALSE;
while ( !found )
{
if ( !AudioFormatReadMP3File ( file, &tformat, NULL ) )
{
break;
}
if ( AudioFormatSame ( &tformat, format ) )
{
found = TRUE;
break;
}
file->seek ( 1, File::CURRENT );
}
if ( !found )
{
pos = 0;
}
else
{
pos = file->seek ( 0, File::CURRENT ) - data_start ;
}
}
else
{
switch ( format->Compression )
{
case AUDIO_COMPRESS_NONE:
block_size = format->Channels*format->SampleWidth;
break;
case AUDIO_COMPRESS_IMA_ADPCM:
case AUDIO_COMPRESS_MS_ADPCM:
block_size = format->cdata.adpcm.BlockSize;
break;
}
if ( block_size > 1 )
{
pos = ( pos / block_size ) *block_size;
}
}
}
file->seek ( pos + data_start, File::START );
return pos;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioFormatSame ( const AudioFormat *f1, const AudioFormat *f2 )
{
if ( (f1->Rate != f2->Rate )
|| (f1->Compression != f2->Compression )
|| (f1->SampleWidth != f2->SampleWidth )
|| (f1->Channels != f2->Channels )
|| (f1->Flags != f2->Flags ) )
{
return FALSE;
}
if ( f1->Compression == AUDIO_COMPRESS_IMA_ADPCM && (f1->cdata.adpcm.BlockSize != f2->cdata.adpcm.BlockSize))
{
return FALSE;
}
if ( f1->Compression == AUDIO_COMPRESS_MP3 && (f1->cdata.mp3.Header != f2->cdata.mp3.Header))
{
return FALSE;
}
return TRUE;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,220 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Dune Emperor **
** **
** Module: <module> (<prefix>_) **
** **
** Version: $ID$ **
** **
** File name: audtimer.cpp **
** **
** Created by: 04/??/99 TR **
** **
** Description: <description> **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <windows.h>
#include <mmsystem.h>
#include <assert.h>
#include <wpaudio/time.h>
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
static TimeStamp lastTime = 0;
static TimeStamp interval = 0;
static TimeStamp timeOut = 0;
static TimeStamp (*timerFunc)( void ) = NULL;// set to either highResGetTime or
// failsafeGetTime at initialization
static LONGLONG timerMilliSecScale; // res counter millisecond scaling factor
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
static TimeStamp failsafeGetTime( void );
static TimeStamp highResGetTime( void );
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
static TimeStamp highResGetTime( void )
{
LARGE_INTEGER count;
union
{
TimeStamp timeStamp;
LARGE_INTEGER largeInt;
}
myTime;
// read the high res counter
QueryPerformanceCounter(&count);
// convert high res ticks to number of milliseconds
myTime.largeInt.QuadPart = count.QuadPart / timerMilliSecScale;
return myTime.timeStamp;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static TimeStamp failsafeGetTime( void )
{
static volatile TimeStamp time = (TimeStamp) 0x100000000; // last time value
static unsigned int* const pl = (unsigned int* const) &time; // low word of time
static unsigned int* const ph = (unsigned int* const) ((unsigned int)&time + 4); // high word of time
unsigned int now, lw, hw, called;
static volatile unsigned int calls = 0;
calls++;
retry:
called = calls;
hw = *ph; // read high word of time stamp
lw = *pl; // read low word of time stamp
if ( called != calls)
{
// AudioGetTime() has been re-entered. Cannot trust lw and lh values
goto retry;
}
reread:
now = timeGetTime ();
if ( now < lw )
{
// wrap round
hw++; // increment high word by one
}
*ph = hw;
*pl = now;
if ( called != calls )
{
// re-entered. cannot trust *ph and *pl to be correct
called = calls;
goto reread;
}
return time;
}
/*****************************************************************************
** Public Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
//
// Initialize the high resolution timer by querying the system for it's
// availability. If one does exist then we set the game timer function
// to 'highResGetTime' otherwise we use the original code at 'failsafeGetTime'.
// For the hi res counter we precalculate the millisecond scaling factor to
// convert hi res ticks to millisecond usage.
void InitAudioTimer( void )
{
LARGE_INTEGER freq;
// does hardware exist for high res counter?
if (QueryPerformanceFrequency(&freq))
{
// calculate timer ticks per second
timerMilliSecScale = freq.QuadPart / (LONGLONG) SECONDS(1);
timerFunc = highResGetTime;
}
else
{
// no high res timer, use old code instead
timerFunc = failsafeGetTime;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
TimeStamp AudioGetTime( void )
{
return timerFunc ? timerFunc() : 0 ;
}

View file

@ -0,0 +1,508 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: WPAudio
//
// Module: AUD
//
// File name: AUD_Windows.cpp
//
// Created: 5/09/01
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mmreg.h>
#include <wpaudio/thread.h>
#include <wpaudio/memory.h>
#include <wpaudio/Source.h>
#include <wsys/File.h>
#include <wpaudio/device.h>
#include <wpaudio/profiler.h>
#include <wpaudio/win32.h>
// 'assignment within condition expression'.
#pragma warning(disable : 4706)
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
struct _aud_thread
{
char name[200];
volatile int quit; /* thread must quit */
volatile int count;
volatile int leaving; /* thread is quiting */
volatile TimeStamp interval; /* itask interval */
volatile int running; /* thread is running */
HANDLE handle; /* threads handle (windows) */
void *data;
AUD_ThreadCB *code;
DWORD id; /* thread id (windows) */
CRITICAL_SECTION access;
AudioServiceInfo update;
ProfileCPU cpu;
DBG_TYPE()
};
DBG_DECLARE_TYPE ( AUD_Thread )
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
static HWND audioMainWindowHandle = NULL;
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
static DWORD WINAPI AUD_service_thread ( VOID *data );
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
static DWORD WINAPI AUD_service_thread ( VOID *data )
{
AUD_Thread *thread = (AUD_Thread *) data;
if ( !thread )
{
return 0;
}
AUD_ThreadBeginCriticalSection ( thread );
thread->running = TRUE;
thread->leaving = FALSE;
while ( !thread->quit )
{
if ( thread->code ( thread, thread->data ))
{
AUD_ThreadEndCriticalSection ( thread );
Sleep ( (unsigned long ) thread->interval );
AUD_ThreadBeginCriticalSection ( thread );
}
else
{
AUD_ThreadEndCriticalSection ( thread );
Sleep ( MSECONDS(5));
AUD_ThreadBeginCriticalSection ( thread );
}
thread->count++;
}
AUD_ThreadEndCriticalSection ( thread );
thread->leaving = TRUE;
return 0;
}
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
AUD_Thread* AUD_ThreadCreate ( const char *name, AUD_ThreadPriority pri, AUD_ThreadCB *code )
{
AUD_Thread *thread;
ALLOC_STRUCT ( thread, AUD_Thread );
DBG_SET_TYPE ( thread, AUD_Thread );
if ( !name )
{
name = "no name given";
}
strncpy ( thread->name, name, ArrayLen(thread->name));
ArrayEnd(thread->name);
thread->quit = FALSE;
thread->leaving = FALSE;
thread->running = FALSE;
thread->count = 0;
thread->code = code;
AudioServiceInfoInit ( &thread->update );
ProfileCPUInit ( thread->cpu );
InitializeCriticalSection ( &thread->access );
AUD_ThreadSetInterval ( thread, SECONDS(1)/30 );
if ( !(thread->handle = CreateThread ( NULL, 4*1024, AUD_service_thread, thread, 0, &thread->id )))
{
DBGPRINTF (( "ERROR: Failed to create audio thread: '%s'\n", thread->name ));
return NULL;
}
int set;
switch (pri)
{
case AUD_THREAD_PRI_NORMAL:
set = TRUE;
break;
case AUD_THREAD_PRI_HIGH:
set = SetThreadPriority ( thread->handle, THREAD_PRIORITY_HIGHEST );
break;
case AUD_THREAD_PRI_REALTIME:
set = SetThreadPriority ( thread->handle, THREAD_PRIORITY_TIME_CRITICAL );
break;
default:
DBG_MSGASSERT ( FALSE, ("Illegal thread priority"));
set = TRUE;
}
if ( !set )
{
char buffer[1024];
FormatMessage ( FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, buffer, sizeof(buffer), NULL);
DBGPRINTF (( "Unable to change the priority of the thread - %s\n", buffer ));
msg_assert ( FALSE, ( "Unable to change the priority of the thread - %s\n", buffer ));
}
DBGPRINTF (( "Created audio thread: '%s'\n", thread->name ));
return thread;
}
//============================================================================
// AUD_ThreadDestroy
//============================================================================
void AUD_ThreadDestroy ( AUD_Thread *thread )
{
DBG_ASSERT_TYPE ( thread, AUD_Thread );
thread->quit = TRUE;
while ( !thread->leaving );
WaitForSingleObject ( thread->handle, SECONDS(5));
CloseHandle ( thread->handle );
DeleteCriticalSection ( &thread->access );
DBGPRINTF (( "Removed audio thread: '%s'\n", thread->name ));
DBG_INVALIDATE_TYPE ( thread );
AudioMemFree ( thread );
}
//============================================================================
// AUD_ThreadBeginCriticalSection
//============================================================================
void AUD_ThreadBeginCriticalSection ( AUD_Thread *thread)
{
DBG_ASSERT_TYPE ( thread, AUD_Thread );
EnterCriticalSection ( &thread->access );
}
//============================================================================
// AUD_ThreadEndCriticalSection
//============================================================================
void AUD_ThreadEndCriticalSection ( AUD_Thread *thread )
{
DBG_ASSERT_TYPE ( thread, AUD_Thread );
LeaveCriticalSection ( &thread->access );
}
//============================================================================
// AUD_ThreadSetData
//============================================================================
void AUD_ThreadSetData ( AUD_Thread *thread, void *data )
{
DBG_ASSERT_TYPE ( thread, AUD_Thread );
AUD_ThreadBeginCriticalSection ( thread );
thread->data = data;
AUD_ThreadEndCriticalSection ( thread );
}
//============================================================================
// AUD_ThreadSetInterval
//============================================================================
void AUD_ThreadSetInterval ( AUD_Thread *thread, TimeStamp interval )
{
DBG_ASSERT_TYPE ( thread, AUD_Thread );
AUD_ThreadBeginCriticalSection ( thread );
thread->interval = interval;
AudioServiceSetInterval ( &thread->update, thread->interval );
AudioServiceSetMustServiceInterval ( &thread->update, thread->interval*4 );
AudioServiceSetResetInterval ( &thread->update, thread->interval*4 );
AUD_ThreadEndCriticalSection ( thread );
}
//============================================================================
// AUD_ThreadGetInterval
//============================================================================
TimeStamp AUD_ThreadGetInterval ( AUD_Thread *thread )
{
DBG_ASSERT_TYPE ( thread, AUD_Thread );
return thread->interval;
}
//============================================================================
// AUD_ThreadName
//============================================================================
char* AUD_ThreadName( AUD_Thread *thread )
{
DBG_ASSERT_TYPE ( thread, AUD_Thread );
return thread->name;
}
//============================================================================
// AUD_ThreadCPUProfile
//============================================================================
ProfileCPU* AUD_ThreadCPUProfile( AUD_Thread *thread )
{
DBG_ASSERT_TYPE ( thread, AUD_Thread );
return &thread->cpu;
}
//============================================================================
// AUD_ThreadAudioServiceInfo
//============================================================================
AudioServiceInfo* AUD_ThreadServiceInfo( AUD_Thread *thread )
{
DBG_ASSERT_TYPE ( thread, AUD_Thread );
return &thread->update;
}
//============================================================================
// AudioFormatReadWaveFile
//============================================================================
int AudioFormatReadWaveFile ( File *file, AudioFormat *format, int *bytes )
{
RIFF_HEADER rh;
RIFF_CHUNK chunk;
WAVEFORMATEX *wformat = NULL;
int got_format = FALSE;
int result = FALSE;
//int mp3 = FALSE;
DBG_ASSERT_TYPE ( format, AudioFormat );
if ( bytes )
{
*bytes = 0;
}
if ( !file )
{
goto done;
}
file->seek ( 0, File::START );
/* read wav info */
if ( file->read ( &rh, sizeof (rh)) != sizeof(rh) )
{
DBGPRINTF (( "error: cannot read file\n" ));
goto done;
}
if ( rh.form != vRIFF || rh.type != vWAVE )
{
file->seek ( 0, File::START );
result = AudioFormatReadMP3File ( file, format, bytes );
goto done;
}
while ( file->read ( &chunk, sizeof (chunk) ) == sizeof(chunk) )
{
switch ( chunk.type )
{
case vFMT :
if ( chunk.length < sizeof ( WAVEFORMATEX ) )
{
wformat = (WAVEFORMATEX *) malloc ( sizeof(WAVEFORMATEX));
memset ( wformat, 0, sizeof ( WAVEFORMATEX) );
}
else
{
wformat = (WAVEFORMATEX *) malloc ( chunk.length );
memset ( wformat, 0, chunk.length );
}
file->read ( wformat, chunk.length );
wformat->cbSize = (ushort) chunk.length;
got_format = TRUE;
break;
case vDATA:
*bytes = chunk.length;
goto got_data;
default:
file->seek ( chunk.length, File::CURRENT );
break;
}
}
DBGPRINTF (( "no data chunk found\n" ));
goto done;
got_data:
if ( !wformat )
{
DBGPRINTF (( "no format chunk found\n" ));
goto done;
}
format->SampleWidth = wformat->wBitsPerSample / 8;
if ( wformat->wFormatTag == WAVE_FORMAT_IMA_ADPCM )
{
format->cdata.adpcm.BlockSize = wformat->nBlockAlign;
format->Compression = AUDIO_COMPRESS_IMA_ADPCM;
format->SampleWidth = 2;
}
else if ( wformat->wFormatTag == WAVE_FORMAT_ADPCM )
{
ADPCMWAVEFORMAT *aformat = (ADPCMWAVEFORMAT *)wformat;
if ( aformat->wNumCoef != 7 && memcmp ( aformat->aCoef, MSADPCM_StdCoef, sizeof ( MSADPCM_StdCoef )) )
{
//currently we only support MS ADPCM using the standard coef table
goto done;
}
format->cdata.adpcm.BlockSize = wformat->nBlockAlign;
format->Compression = AUDIO_COMPRESS_MS_ADPCM;
format->SampleWidth = 2;
}
else if ( wformat->wFormatTag == WAVE_FORMAT_PCM )
{
format->Compression = AUDIO_COMPRESS_NONE;
}
else if ( wformat->wFormatTag == WAVE_FORMAT_MPEGLAYER3 )
{
result = AudioFormatReadMP3File ( file, format, NULL );
goto done;
}
else
{
goto done;
}
format->Channels = wformat->nChannels;
format->BytesPerSecond = wformat->nAvgBytesPerSec;
format->Rate = wformat->nSamplesPerSec;
format->Flags = mAUDIO_FORMAT_PCM;
AudioFormatUpdate ( format );
result = TRUE;
done:
if ( wformat )
{
free ( wformat );
}
return result;
}
//============================================================================
// WindowsDebugPrint
//============================================================================
void WindowsDebugPrint( const char * lpOutputString )
{
OutputDebugStringA ( lpOutputString );
}
//============================================================================
// AudioSetWindowsHandle
//============================================================================
void AudioSetWindowsHandle ( HWND hwnd )
{
audioMainWindowHandle = hwnd;
}
//============================================================================
// AudioGetWindowsHandle
//============================================================================
HWND AudioGetWindowsHandle ( void )
{
return audioMainWindowHandle;
}

View file

@ -0,0 +1,377 @@
# Microsoft Developer Studio Project File - Name="WPAudio" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Static Library" 0x0104
CFG=WPAudio - 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 "WPAudio.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 "WPAudio.mak" CFG="WPAudio - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "WPAudio - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "WPAudio - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE "WPAudio - Win32 Internal" (based on "Win32 (x86) Static Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName "WPAudio"
# PROP Scc_LocalPath "..\.."
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "WPAudio - 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 Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /W3 /WX /GX /O2 /Ob2 /I "..\..\include" /D "_LIB" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_RELEASE" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
!ELSEIF "$(CFG)" == "WPAudio - 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 Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /WX /Gm /GX /ZI /Od /I "..\..\include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
!ELSEIF "$(CFG)" == "WPAudio - Win32 Internal"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Internal"
# PROP BASE Intermediate_Dir "Internal"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Internal"
# PROP Intermediate_Dir "Internal"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /W3 /WX /GX /O2 /Ob1 /I "..\..\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "_INTERNAL" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
!ENDIF
# Begin Target
# Name "WPAudio - Win32 Release"
# Name "WPAudio - Win32 Debug"
# Name "WPAudio - Win32 Internal"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\AUD_Assert.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Attributes.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Cache.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Channel.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Device.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_DSoundDriver.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Events.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Level.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_List.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Lock.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Memory.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Profiler.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Source.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_StreamBuffering.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Streamer.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Time.cpp
# End Source File
# Begin Source File
SOURCE=.\AUD_Windows.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=..\..\Include\WPAudio\Altypes.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Attributes.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Cache.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Channel.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Debug.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Defs.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Device.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Driver.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Errors.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Events.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Handle.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Level.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\List.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Lock.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Memory.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Profiler.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Search.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Source.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\StreamBuffering.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Streamer.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Thread.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Time.h
# End Source File
# Begin Source File
SOURCE=..\..\Include\WPAudio\Win32.h
# End Source File
# End Group
# Begin Group "Asimp3"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\Asimp3\Asimp3asm.asm
!IF "$(CFG)" == "WPAudio - Win32 Release"
# Begin Custom Build
InputDir=.\Asimp3
OutDir=.\Release
InputPath=.\Asimp3\Asimp3asm.asm
BuildCmds= \
$(InputDir)\bin\ml /nologo /coff /Cp /Zm /c /W2 /Zi /Fo$(OutDir)\asi_x86.obj $(InputPath) \
$(InputDir)\bin\ml /nologo /coff /Cp /Zm /c /W2 /Zi /DAMD /Fo$(OutDir)\asi_amd.obj $(InputPath) \
"$(OutDir)\asi_x86.obj" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
"$(OutDir)\asi_amd.obj" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
# End Custom Build
!ELSEIF "$(CFG)" == "WPAudio - Win32 Debug"
# Begin Custom Build
InputDir=.\Asimp3
OutDir=.\Debug
InputPath=.\Asimp3\Asimp3asm.asm
BuildCmds= \
$(InputDir)\bin\ml /nologo /coff /Cp /Zm /c /W2 /Zi /Fo$(OutDir)\asi_x86.obj $(InputPath) \
$(InputDir)\bin\ml /nologo /coff /Cp /Zm /c /W2 /Zi /DAMD /Fo$(OutDir)\asi_amd.obj $(InputPath) \
"$(OutDir)\asi_x86.obj" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
"$(OutDir)\asi_amd.obj" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
# End Custom Build
!ELSEIF "$(CFG)" == "WPAudio - Win32 Internal"
# Begin Custom Build
InputDir=.\Asimp3
OutDir=.\Internal
InputPath=.\Asimp3\Asimp3asm.asm
BuildCmds= \
$(InputDir)\bin\ml /nologo /coff /Cp /Zm /c /W2 /Zi /Fo$(OutDir)\asi_x86.obj $(InputPath) \
$(InputDir)\bin\ml /nologo /coff /Cp /Zm /c /W2 /Zi /DAMD /Fo$(OutDir)\asi_amd.obj $(InputPath) \
"$(OutDir)\asi_x86.obj" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
"$(OutDir)\asi_amd.obj" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
$(BuildCmds)
# End Custom Build
!ENDIF
# End Source File
# Begin Source File
SOURCE=.\Asimp3\Datatbl.h
# End Source File
# Begin Source File
SOURCE=.\Asimp3\Imssapi.h
# End Source File
# Begin Source File
SOURCE=.\Asimp3\Mp3api.cpp
# End Source File
# Begin Source File
SOURCE=.\Asimp3\Mp3dec.cpp
# End Source File
# Begin Source File
SOURCE=.\Asimp3\Mp3dec.h
# End Source File
# Begin Source File
SOURCE=.\Asimp3\Mss.h
# End Source File
# Begin Source File
SOURCE=.\Asimp3\Mssasi.h
# End Source File
# Begin Source File
SOURCE=.\Asimp3\Rib.h
# End Source File
# End Group
# End Target
# End Project

View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View file

@ -0,0 +1,25 @@
This document is going to be a place to put descriptions of our reasoning for any "confusing" design decisions made during the conversion to DX8.
TODO: WRITE PROPER DESCRIPTIONS FOR ALL OF THE FOLLOWING!!!
Lighting
--------
The user has two choices regarding lighting. SimpleSceneClass will just install the first four LightClasses
into DX8 and let them affect all objects. Otherwise, you need to implement a SceneClass which wraps each
top-level render object and associates a LightEnvironmentClass with it. In your 'Customized_Render' function
you need to update each object's LightEnvironment object from the lights around it.
Mesh Rendering System
---------------------
Grouped by FVF format and material settings. Many meshes can share a single VB and IB...
MeshModel Sharing
-----------------
It would be nice if MeshClass just Add_Ref'd the same mesh model whenever we re-use a mesh. The problems with
this are:
- the transform which is used during deffered rendering and during decal rendering
- instance specific light environment pointer is in the mesh model class
- lots of other stuff <-- FILL THIS IN!

View file

@ -0,0 +1,274 @@
Now we've got WW3D up and running with the skeleton app. Here is a somewhat more detailed/organized
list of what is left to do:
LIMITATIONS/POTENTIAL PROBLEMS:
- Skins need to use a limited set of rendering features?
- Polys going into the alpha system must use a limited set of rendering features.
- Additional procedural material passes cannot be applied to alpha blended meshes (no multi-pass alpha)
- Meshes only get two vertex color arrays. Their vertex materials must be configured properly to enable them.
- HY: Simplescene supports only four global lights
- HY: WW3D Spoltlights have an approximate inverse linear attentuation instead of max linear attentuation
UPDATED TODO LIST:
agg_def:
- Texture replacement feature needs to be implemented
assetmgr:
- Texture cache,
- Dazzle loader,
bmp2d
- works. Code for setting aspect ratio was added but not used due to legacy - HY
boxrobj
- Alpha blending support needed
- Vis rendering code needed
camera
- *** CameraClass projection convention changed to -1<x<1... Fix all code in commando and E&B!
- Also convert all uses of CameraClass::Un_Project!
cullablematpass
- collapsed into matpass
dazzle
- Fully commented out
decalmsh
- works
decalsys
- DistAlphaVP to be converted (renegade)
dx8wrapper
- Toggle_Windowed function commented out
- One-time init functions commented out (TextureLoader,SegmentedLine,Dazzle)
dynamesh
- works except for alpha support
font3d
- works
intersec
- Compiles but not tested,
- Probably contains a lot of un-used functions because all intersect calls are now routing through Cast_Ray!
line3d
- works except for alpha sorting
mapper
- All mappers need to properly handle the u-v array index
- Animating1DMapper commented out
- Axial mapper commented out
- Silhouette mapper commented out
matrixmapper
- All mappers need to properly handle the u-v array index
matinfo
- Texture reduction factor access commented out
matpass
- works except for skins and culling
mesh
- static sort list code commented out
- vis rendering code commented out
- ORIENTED and ALIGNED features broken
- dependencies code commented out
meshmdl
- shadow rendering
- vis rendering
- get_deformed_screenspace_vertices commented out (Can we delete this? skins are working...)
- get_deformed_eyespace_vertices commented out (Can we delete this? skins are working...)
- Render_Prelit
- needs to use the dynamic index buffer when rendering cullable material passes
meshmdlio
- animated texture loading not implemented
- use mesh TWOSIDED flag to set shaders on loading (and take out Jani's temp code to do this on the fly)
metalmap
- fully commented out
pointgr
- works except for alpha support
r3dobj
- fully commented out - may be able to DELETE
render2d
- works
rendobj
- texture_reduction feature commented out - may not be needed (only have a global texture reduction factor?)
rinfo
- surrender-specific vis code
ringobj
- works
- no alpha support
- vis rendering code commented out
scene
- static sort list code commented out
segline
- material handling code commented out
- rendering code commented out
shattersystem
- works, but might not for multi-texturing.
- Naty check if dynamesh supports multi-texturing. you changed the interface
- a bit. HY
sphereobj
- works
- no alpha support
- no viz
- might have bug in sphere generation when sphere is too big
sr_util
- DELETED!
statistics
- fully commented out
texfcach
- fully commented out
texproject
- works except for the second stage stuff (is this generally broken?).
textdraw
- works. This is a legacy font system to be removed from Renegade.
texture
- lots of miscellaneous missing features here
- NoLOD flag to be supported for UI textures
texturefile
- fully commented out
textureloader
- fully commented out, this is the background loading system
txt.cpp
- works. This is a legacy font system to be removed from E&B.
txt2d.cpp
- works. This is a legacy font system to be removed from E&B.
vertmaterial
- all uses of VertexMaterialClass may need to configure the UV-array source index.
- all users of VMC must properly enable/disable lighting
- all users of VMC must configure the diffuse and emissive color source if they want per-vertex arrays...
- changed so the default is visible - HY
- TODO: Make Get_Preset const so no one can modify the static presets
ww3d
- TextureLoader support commented out of Begin_Render
- Statistics code commented out
- Read_Gerd_Render_Device_Description commented out (?)
- Set/Get_Texture_Reduction commented out
- Flush_Texture_Cache function commented out
- Detect_Drivers commented out
- On_Activate/Deactivate_App commented out
- Update_Pixel_Center commented out
General:
- Add support for multiple texture stages
- Create sort system
- Add support for exclusion masks. The way this works is that we support exclusion masks differently at the top-level object level and the low-level rendering object level (note that this means that the mid-levels will get skipped over unless we collapse the container bits together with the low-level object bits. However we probably cannot do this, since the subobjects bits may be more permissive than the container's bits. Should we worry about this?). At the top level, when the light environment is constructed, we filter the light's exclusion bits vs. the light environments' exclusion bits (passed into the construct call, stored in the light environment at creation, or something). If it does not pass, it is not inserted into the light environment. When we insert a light into the light environment, we also store its exclusion bits (perhaps we only do this if "light aggregation" is turned off - see below). Then the low-level code for applying the current light environment receives the low-level robj's exclusion bits, compares them, and decides whether to actually set the light into D3D. For this to work properly, we need a flag to prevent lights which are rejected from the light environment because it is full of more-important lights from contributing to the ambient.
- Change the "Get_Texture_Array" functions in MeshMatDescClass to private, change name to PEEK!
- have texture category sorting functions take account of both texture stages
NOTE: We have three enums/#defines/consts for the number of texture stages:
RENDERER_MAX_TEX_STAGES, MeshMatDescClass::MAX_TEX_STAGES, MeshBuilderClass::MAX_STAGES.
We are even explicitly comparing two of them and asserting if they are different in
dx8renderer.cpp. There is a similar situation with DX8FVFCategoryContainer::MAX_PASSES,
MeshMatDescClass::MAX_PASSES and MeshBuilderClass::MAX_PASSES. We need to clean this up!
Features we are removing support for (will need application changes):
- indirect textures (we will provide a (slow) function to swap textures)
Ok, the WW3D2 skeleton program compiles and links now with no references to SR. Some files I took more care on, others I blocked out the entire file for later. Every piece of code is wrapped with #ifdef WW3D_DX8 with a matching #endif and comment, there should be no typo's in the #ifdef's because I used a macro to insert them all (so we can do searches and safely assume that we see all of them).
Here are some (raw) notes I took while commenting out all references to SR:
X - VertexMaterialClass - need to separate from sr (SOON)
X - Basic texture functionality (loading from files, etc)
- More advanced texture handling code needs to be re-implemented, but try to maintain our current interfaces as much as possible. Many issues here: background thread loader, texture file cache, texture LOD, indirect textures ?... (SOON)
X - ShaderClass - just convert to our own, mimicing the bitfield that SR used for now (SOON)
- Exclusion masks - this is not supported but some application code is probably using it. We need to decide what to do here.
- Camera aligned and camera oriented meshes - the way we used to do them required the mesh push transform code to change the camera matrix, which doesn't work well with our current scheme. We need to think how to support this stuff.
X - MaterialInfo Class - can un-comment when we have the above material systems in place, evaluate where this is used?
- sr_util - all of these functions un-needed? (no more As_srVector3 :-)
- agg_def.cpp - all texture features commented out
X - box render objects completely commented out - materials, gerd
- texturefcache - commented out
- TextureFileClass - commented out
X - DynaMeshClass - commented out, need materials, gerd replacement, new rendering interface before we can bring this back. A *lot* of stuff uses DynaMeshClass... (shatter system, text systems, etc)
- Dazzles - commented out, gerd, materials
- streaming textures - commented out, new texture code needed
- texture creation functions in assetmanager commented out
X - Bitmap2DObjClass - commented out, do we need this any more? - depends on dynamesh
X - BW Render, used for shadow rendering, uses some srVectorProcessor stuff
X - Camera - internals commented out
X - MatPass, CullableMatPass - all procedural material passes commented out, design new, low-level rendering interface, then port these to it.
X - passdata.h - no sure what this is for, it is commented out
X - meshmodel - all material functions commented out, will also probably re-write the guts of meshmodel to store "sub-meshes" rather than material arrays. depends on design of new low-level rendering system. (SOON)
X - decal system - all material handling commented out, rendering commented out, depends on new low-level rendering system
X - matrix mapper - this is used to generate u-v coordinates in a surrender vertex processor, new system needs to support this functionality, then we can port this (well, it will use DX8 features to do the same thing in HW...)
X - intersec.h - commented out, Eric Cosky's collision code, do we need this any more?
X - dx8renderer - uses srVectorProcessor
X - dx8wrapper - uses Gerd
X - fog.cpp - all fog objects commented out, probably won't implement fog with a render object, just have fog settings be stored in the scene
X - font3d - surface loading! textures
X - lightclass - hmmmm, re-consider lighting...
X - lightenv - uses lightclass as input.
X - line3d - uses srModeller, srMeshModel. Do we need this any more?
X - mapper.cpp - srVertexProcessor, need to convert to HW form (DELETED)
X - mesh - materials, gerd transforms, meshmodel
X - meshgeometry - uses srVectorProcessor in a couple of places
X - meshmdl - lots of material and gerd stuff, re-design?
- metalmap - uses vectorprocessor, textures
X - pointgroups - surrender types, gerd rendering, materials
X - particle buffer - needs point group
X - particle emitter - materials, particle buffer
X - particle loader - needs particle classes, materials
X - polyinfo - don't know what this is?
X - projector - built on top of matrix mapper, used for projected textures
X - r3dobj - image3dobj, can we delete this?
X - render2d - textures, gerd calls
X - rendertype - lots of GERD stuff, Jani is this an "obsolete" optimization?
- rinfo - material passes, gerd stuff, vis commands which use ext-functions for the surrender software rasterizer.
X - ring render object - materials, gerd rendering calls
X - scene - fog settings, don't use FogClass any more, just plug fog settings into the new low-level interface...
- scene - ensure that the scene ambient light value gets added into the light environment's effective ambient (E&B uses the scene ambient and currently it doesn't do anything)
- segline - custom gerd pipeline, materials
X - shatter system - dynamic meshes, materials.
X - sphere - materials, gerd rendering calls
- statistics - Jani's texture stats, new texture system must support this.
- texfcache - completely commented out
X - texproject - needs procedural material pass interface, vertex processor...
X - textdraw - textures, dynamic meshes
- texture loader - is this the backround thread loader? commented out
X - txt.cpp - uses textures
X - txt2d.cpp - uses txt, dynameshes
X - vertexbuffer.cpp - not sure what this is, uses srVectorProcessor
- ww3d - lots of gerd calls, surrender types, screen capture, movie capture, texture reduction, statistics
- Investigate cost of BeginScene-EndScene, set viewport has to occur right after beginScene... Should we minimize our viewport changes? If the cost is high, we might need to separate the viewport definition from the camera...
- Silhouette mapper un-implemented
- Axial mapper un-implemented
- How should handle the extra render states for dealing with TexGen? If we apply these states, there is nothing to remove them!
-- HY render states for texgen and texmaterial are encapsulated in vertex material & mapper
-- if the vertex material detects a null mapper the defaults are set.
- Make Dynamesh support Cast_Ray so that the E&B intersection code works!
Shaders have been converted to Shader->Apply() and the state is being tracked by the shader class.
Materials have been converted to Material->Apply() which calls Mapper->Apply()
Now, some big issues we need to think about are (in no particular order):
SOME GOALS - Maintain as many existing class interfaces as we can so that our apps don't have much re-coding!
Input data is existing W3D files, no format changes or new tools! (for now anyway :-)
Incorporate the ideas in Jani's DX8 code, grouping small meshes together in larger VBs, etc
GERD Replacement - our new low-level rendering interface needs to be the replacement for everywhere we are using the GERD. It needs to supply all of the features that we are using in the GERD currently while allowing us to use Jani's mesh optimizations. There is a lot to think about here and almost everything depends on us doing this right. It also might need to serve as an abstraction layer so that I can implement a software VIS-renderer behind it.
SOLUTION: No GERD replacement, no abstraction layer. DX8Wrapper will be a thin insulation layer though.
VIS - I'll have to re-implement the rendering code for VIS. It will be a 32bit, solid fill, zbuffering software rasterizer. Not too hard but still a bit of work. Needs to be done in a way that all render objects can be rendered through it easily.
SOLUTION: Vis rendering will branch at the render object level to a new custom vis rasterizer possibly based on the bw shadow code
TEXTURES - there are many features in the textures system, the "file cache", background loading, LOD, tga loading, global texture reduction factor, etc.
SOLUTION: Naty is working on this system.
MESHMODEL - I think we'll need to do some load-time conversion of all of our mesh-models into a form similar to Jani's dx8 mesh code. More thought needs to be put into this.
ALPHA SORTING - This issue becomes especially complex when you think about mixing different types of render objects. This issue will influence how we design the low level rendering interface.
SOLUTION: There will be a custom alpha sorting/rendering system. RenderObjects will be allowed to talk to either DX8 or this system.
UNIX - It looks like Neal has put some effort into making ww3d compile under Unix. We will probably make life very difficult for him if there are DX8 calls all over the place :-)
SOLUTION: UNIX version of E&B will not use WW3D2. It only needs collision detection so we'll implement a custom solution.
Optimizations:
==============
* Currently we search a linear list for textures with the same name every time we ask
the asset manager for a new texture. We should probably use the hashtable template
for this instead.
* The VectorProcessorClass stuff is completely unoptimized - we might want to
optimize this in future.
********* NEW STUFF ************
DX8 Features which we currently do not support and might want to in future:
* Different modes than clamp and wrap - DX8 exposes all kinds of cool modes like mirror, mirror once - do we expose these in our tools? How well are they supported in hardware? If they are not universally supported then we probably don't want to expose them...
* Cube and volume textures - support via inheriting from shared base class with TextureClass?
* Anisotropic filtering: currently trilinear is enabled in the TextureClass but not in our tools. There are issues to think about for both trilinear and anisotropic like: not all hardware supports it, some hardware supports it if there is just one texture but not otherwise, or with a slowdown etc. - this is probably not something we want the artist to specify directly: perhaps the artist can mark those meshes which would benefit from "advanced filtering", and we turn it on selectively depending on hardware?
We can do fancy texture matrix operations "for free" now. (Only on Hardware T&L cards!)

View file

@ -0,0 +1,7 @@
- Detect skins that are using materials that are too complex (max of two uv channels, one pass???)
- Remove Translucency from max plugin
- Editor has to resolve all of the cases where it is rendering to secondary windows
- VIS system needs a new z-buffering software renderer
- WWConfig needs to be updated
- Classic environment mapper and env mapper are the same thing now...

View file

@ -0,0 +1,191 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// FramGrab.cpp: implementation of the FrameGrabClass class.
//
//////////////////////////////////////////////////////////////////////
#include "framgrab.h"
#include <stdio.h>
#include <io.h>
//#include <errno.h>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
FrameGrabClass::FrameGrabClass(const char *filename, MODE mode, int width, int height, int bitcount, float framerate)
{
HRESULT hr;
Mode = mode;
Filename = filename;
FrameRate = framerate;
Counter = 0;
Stream = 0;
AVIFile = 0;
if(Mode != AVI) return;
AVIFileInit(); // opens AVIFile library
// find the first free file with this prefix
int counter = 0;
int result;
char file[256];
do {
sprintf(file, "%s%d.AVI", filename, counter++);
result = _access(file, 0);
} while(result != -1);
// Create new AVI file using AVIFileOpen.
hr = AVIFileOpen(&AVIFile, file, OF_WRITE | OF_CREATE, NULL);
if (hr != 0) {
char buf[256];
sprintf(buf, "Unable to open %s\n", Filename);
OutputDebugString(buf);
CleanupAVI();
return;
}
// Create a stream using AVIFileCreateStream.
AVIStreamInfo.fccType = streamtypeVIDEO;
AVIStreamInfo.fccHandler = mmioFOURCC('M','S','V','C');
AVIStreamInfo.dwFlags = 0;
AVIStreamInfo.dwCaps = 0;
AVIStreamInfo.wPriority = 0;
AVIStreamInfo.wLanguage = 0;
AVIStreamInfo.dwScale = 1;
AVIStreamInfo.dwRate = (int)FrameRate;
AVIStreamInfo.dwStart = 0;
AVIStreamInfo.dwLength = 0;
AVIStreamInfo.dwInitialFrames = 0;
AVIStreamInfo.dwSuggestedBufferSize = 0;
AVIStreamInfo.dwQuality = 0;
AVIStreamInfo.dwSampleSize = 0;
SetRect(&AVIStreamInfo.rcFrame, 0, 0, width, height);
AVIStreamInfo.dwEditCount = 0;
AVIStreamInfo.dwFormatChangeCount = 0;
sprintf(AVIStreamInfo.szName,"G");
hr = AVIFileCreateStream(AVIFile, &Stream, &AVIStreamInfo);
if (hr != 0) {
CleanupAVI();
return;
}
// Set format of new stream
BitmapInfoHeader.biWidth = width;
BitmapInfoHeader.biHeight = height;
BitmapInfoHeader.biBitCount = (unsigned short)bitcount;
BitmapInfoHeader.biSizeImage = ((((UINT)BitmapInfoHeader.biBitCount * BitmapInfoHeader.biWidth + 31) & ~31) / 8) * BitmapInfoHeader.biHeight;
BitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER); // size of structure
BitmapInfoHeader.biPlanes = 1; // must be set to 1
BitmapInfoHeader.biCompression = BI_RGB; // uncompressed
BitmapInfoHeader.biXPelsPerMeter = 1; // not used
BitmapInfoHeader.biYPelsPerMeter = 1; // not used
BitmapInfoHeader.biClrUsed = 0; // all colors are used
BitmapInfoHeader.biClrImportant = 0; // all colors are important
hr = AVIStreamSetFormat(Stream, 0, &BitmapInfoHeader, sizeof(BitmapInfoHeader));
if (hr != 0) {
CleanupAVI();
return;
}
Bitmap = (long *) GlobalAllocPtr(GMEM_MOVEABLE, BitmapInfoHeader.biSizeImage);
}
FrameGrabClass::~FrameGrabClass()
{
if(Mode == AVI) {
CleanupAVI();
}
}
void FrameGrabClass::CleanupAVI() {
if(Bitmap != 0) { GlobalFreePtr(Bitmap); Bitmap = 0; }
if(Stream != 0) { AVIStreamRelease(Stream); Stream = 0; }
if(AVIFile != 0) { AVIFileRelease(AVIFile); AVIFile = 0; }
AVIFileExit();
Mode = RAW;
}
void FrameGrabClass::GrabAVI(void *BitmapPointer)
{
// CompressDIB(&bi, lpOld, &biNew, lpNew);
// Save the compressed data using AVIStreamWrite.
HRESULT hr = AVIStreamWrite(Stream, Counter++, 1, BitmapPointer, BitmapInfoHeader.biSizeImage, AVIIF_KEYFRAME, NULL, NULL);
if(hr != 0) {
char buf[256];
sprintf(buf, "avi write error %x/%d\n", hr, hr);
OutputDebugString(buf);
}
}
void FrameGrabClass::GrabRawFrame(void * /*BitmapPointer*/)
{
}
void FrameGrabClass::ConvertGrab(void *BitmapPointer)
{
ConvertFrame(BitmapPointer);
Grab( Bitmap );
}
void FrameGrabClass::Grab(void *BitmapPointer)
{
if(Mode == AVI)
GrabAVI(BitmapPointer);
else
GrabRawFrame(BitmapPointer);
}
void FrameGrabClass::ConvertFrame(void *BitmapPointer)
{
int width = BitmapInfoHeader.biWidth;
int height = BitmapInfoHeader.biHeight;
long *image = (long *) BitmapPointer;
// copy the data, doing a vertical flip & byte re-ordering of the pixel longwords
int y = height;
while(y--) {
int x = width;
int yoffset = y * width;
int yoffset2 = (height - y) * width;
while(x--) {
long *source = &image[yoffset + x];
long *dest = &Bitmap[yoffset2 + x];
*dest = *source;
unsigned char *c = (unsigned char *) dest;
c[3] = c[0];
c[0] = c[2];
c[2] = c[3];
c[3] = 0;
}
}
}

View file

@ -0,0 +1,148 @@
MAPPER_NAME
Description:
What the mapper does
Params:
What to type in the argument space
<Argument Name> = (type) <default value>
e.g. if it says
UPerSec=(float) 0.0;
it means you have to type in
UPerSec=1.0;
to get your U coordinate scrolling at one unit per second and it has to be a float. If you don't type anything the
default value is 0.0.
Types:
(float) - any real number
(int) - any integer
(bool) - either TRUE or FALSE
LINEAR_OFFSET
Description:
Makes the texture scroll at the speed specified
Params:
UPerSec=(float) 0.0;
VPerSec=(float) 0.0;
UScale=(float) 1.0;
VScale=(float) 1.0;
CLASSIC_ENVIRONMENT
Description:
Uses the Normals to look up the environment map
ENVIRONMENT
Description:
Uses the Reflection direction to look up the environment map
SCREEN
Description:
Projects takes the screen coordinate as the UV coordinate
Params:
UScale=(float) 1.0f;
VScale=(float) 1.0f;
SILHOUETTE
Description: Obsolete, not supported
SCALE
Description:
Scales the UV coordinates. Useful for detail mapping.
Params:
UScale=(float) 1.0f;
VScale=(float) 1.0f;
GRID
Description:
Given a texture that is divided up in to a grid, it animates the texture by looking
up the texture from the topleft to the bottom right, going left to right and then
top to bottom (the same way you would read English text). The texture map must be divided
up evenly.
Params:
FPS=(float) 1.0f; The frames per second
Log2Width=(int) 1; So 0=width 1, 1=width 2, 2=width 4. The default means animate using a texture divided up into quarters.
Last=(int) GridWidth*GridWidth; The last frame to use
ROTATE
Description:
Rotates a texture map counterclockwise about a specified center then scales the texture
Params:
Speed=(float) 0.1f; In Hertz. 1 = 1 rotate per second
UCenter=(float) 0.0f;
VCenter=(float) 0.0f;
UScale=(float) 1.0;
VScale=(float) 1.0;
SINE_LINEAR_OFFSET
Description:
Moves the texture map in the shape of a Lissajous figure.
Params:
UAmp=(float) 1.0f;
UFreq=(float) 1.0f;
UPhase=(float) 0.0f;
VAmp=(float) 1.0f;
VFreq=(float) 1.0f;
VPhase=(float) 0.0f;
STEP_LINEAR_OFFSET
Description:
Similar to Linear Offset but moves stuff around in discrete steps
Params:
UStep=(float) 0.0f;
VStep=(float) 0.0f;
SPS=(float) 0.0f; Steps per second
ZIGZAG_LINEAR_OFFSET
Description:
Similar to Linear Offset but reverses direction periodically.
Params:
UPerSec=(float) 0.0f;
VPerSec=(float) 0.0f;
Period=(float) 0.0f; Time it takes to make a zigzag in seconds
WS_CLASSIC_ENVIRONMENT
Description:
World space normal environment map
WS_ENVIRONMENT
Description:
World space reflection environment map
GRID_CLASSIC_ENVIRONMENT
Description:
Animated normal environment map
GRID_ENVIRONMENT
Description:
Animated reflection environtment map
RANDOM
Description: Randomly rotates and translates a texture with linear offset
FPS=(float) 0.0f; Frames per second
UPerSec=(float) 0.0;
VPerSec=(float) 0.0;
EDGE
Description: Uses the Z-coordinate of the reflection or normal vector to access the U coordinate
The V-coordinate is linear offset
VPerSec=(float) 0.0
UseReflect=FALSE
VStart=(float) 0.0
BUMPENV
Description: Sets up and possibly animates the bump matrix, also has the LinearOffset features
NOTE: even if you don't want to animate the bump matrix, you should use this mapper
so that the matrix gets set up with the identity settings.
BumpRotation = (float) 0.1f; In Hertz. 1 = 1 rotate per second (DEFAULT = 0.0)
BumpScale = scale factor applied to the bumps (DEFAULT = 1.0)
UPerSec=(float) 0.0;
VPerSec=(float) 0.0;
UScale=(float) 1.0;
VScale=(float) 1.0;
-----------
TODO:
-the ability to affect the period of the zigzag in only one direction (V) while the other (U) continues to offset undisturbed in the original linear fashion.
-groovy scaling
-scale random
-random mapper random time

View file

@ -0,0 +1,25 @@
HY 2/14/01 Created.
Polygons are rendered via:
A. The Mesh packet renderer
B. Render object's render method
C. Alpha sorting pipeline
NB. Shaders and Vertex Mateirals have presets that you can use to set state
Guidelines for B type polygons:
1. Always set the World matrix
2. Always use a Shader (Shader.Apply())
3. Always use a VertexMaterial (VertexMaterial.Apply())
4. If there is a Texture, use Texture.Apply()
For any other render state, save the initial state, set your state, render then
restore the original state.
e.g. save the VIEW matrix if you're nuking it, then restore it after you're done.
Guidelines for C type polygons:
1. All vertices submitted must be in View Coordinates
2. All states submitted must be entirely contained in Shader,VertexMaterial & Texture

View file

@ -0,0 +1,161 @@
;---------------------------------------------------------------------------------------------------------------------
;
; DAZZLE.INI
; This file is a sample DAZZLE.INI file, indicating how to set up dazzles and lens
; flares for an application. PLEASE USE THIS FOR REFERENCE ONLY. You should consult the
; asset manager of your project in order to find the location of the actual DAZZLE.INI
; file that is specific to your project.
;
; PLEASE NOTE: some of the tools and library code assume that the dazzle type 'DEFAULT'
; exists and is the first dazzle type.
;
;
; DAZZLE DEFINITION PARAMETERS:
;-------------------------------
; After the dot product between the camera direction and the camera space location of the light source,
; we do a power to Halopow for the halo size and power to DazzlePow for the dazzle size. Halo/DazzleArea
; defines the angle where the values are valid, so any angle beyond HaloArea/DazzleArea results the halo or
; dazzle being scaled to zero. The angles are re-scaled from normalized angle scale of (-1.0...1.0)
; only HaloArea/DazzleArea defined part is used.
;
; HaloIntensityPow - 1.0 would be linear fadeout, smaller than that will steepen the curve (smaller hotspot)
; HaloSizePow - 1.0 would be linear fadeout, smaller than that will steepen the curve (smaller hotspot)
; DazzleIntensityPow
; DazzleSizePow
; HaloArea
; DazzleArea - Something like 0.05 is a good starting point for a dazzle...
; HaloScaleX - X scale factor for the halo
; HaloScaleY - Y scale factor for the halo
; DazzleScaleX - X scale factor for the dazzle
; DazzleScaleY - Y scale factor for the dazzle
; HaloIntensity - base intensity of the halo
; DazzleIntensity - base intensity of the dazzle
;
; Direction area defines the maximum difference between the light direction and the eyespace location,
; so the dazzle can only be seen if the camera is inside the light cone. Value 0.0 means dazzle is not
; directional, it can be seen from any direction. Halo is not affected. Dazzle direction defines the light
; direction vector.
;
; DazzleDirectionArea - Something like 0.5 might be a good test value
; DazzleDirection - direction vector (gth - we should lock this paramter to 0,0,1)
; FadeoutStart - distance in meters where the halo and dazzle start to fade out
; FadeoutEnd - distance in meters where the halo and dazzle are completely faded out
; SizeOptimizationLimit- NOT FINISHED
; HistoryWeight - The weight of history for the intensities. The history weight is per millisecond,
; so if you want to have any real history, values higher than 0.95 are recommended
; (don't use value of 1.0 or anything higher!)
; DazzleTextureName - texture filename for the dazzle
; HaloTextureName - texture filename for the halo
; UseCameraTranslation - If false, camera matrix's translation term isn't used. If translation isn't used
; the dazzle is treated as always visible, scene graph visibility is not used.
; HaloColor - color of the halo
; DazzleColor - color of the dazzle
;
; DazzleTestColor - Dazzle test color could be white in many cases but if the level has a lot of
; white then another color can be defined.
; LensflareName - Dazzle can use a lensflare by defining a name of lensflare in the ini
;
;
; LENSFLARE DEFINITION PARAMETERS:
;----------------------------------
;
; TextureName - Texture to be used by the lensflare
; FlareCount - The number of flare sprites in lensflare. The FlareLocation, FlareSize and FlareColor
; paremeters are procedural, the names are produced based on this parameter. If
; FlareCount is 3, there exists FlareLocation1, FlareLocation2 and FlareLocation3... And so on.
; FlareLocation - 1D-locations of the flares. 0.0 means the location of the center of the screen and
; 1.0 means the location of the lightsource. The values can be smaller than zero and
; larger than 1.0.
; FlareSize - Normalized flare sizes
; FlareColor - Colors for each flare sprite
; FlareUV - Uv coordinates (as there can be only one texture used, but one may want multiple images).
; The values is a 4-float vector: start_u, start_v, end_u, end_v.
;
;---------------------------------------------------------------------------------------------------------------------
[Dazzles_List]
0=DEFAULT
1=SUN
[Lensflares_List]
0=SUN_LENSFLARE
;========================================== DAZZLE DEFINITIONS ====================
[DEFAULT]
HaloIntensity=1.0
HaloIntensityPow=0.95
HaloSizePow=0.95
HaloArea=1.0
HaloScaleX=0.2
HaloScaleY=0.2
DazzleArea=0.05
DazzleDirectionArea=0
DazzleDirection=0,1,1
DazzleSizePow=0.9
DazzleIntensityPow=0.9
DazzleIntensity=50
DazzleScaleX=1.0
DazzleScaleY=1.0
FadeoutStart=30.0
FadeoutEnd=40.0
SizeOptimizationLimit=0.05
HistoryWeight=0.975
UseCameraTranslation=1
HaloTextureName=SunHalo.tga
DazzleTextureName=SunDazzle.tga
DazzleColor=1,1,1
HaloColor=1,1,1
DazzleTestColor=1,1,1
[SUN]
HaloSizePow=1.0
HaloIntensityPow=1.0
HaloArea=1.0
HaloScaleX=0.25
HaloScaleY=0.25
DazzleArea=0.04
DazzleDirectionArea=0
DazzleDirection=0,1,1
DazzleSizePow=1.0
DazzleIntensityPow=1.0
DazzleIntensity=1.0
DazzleScaleX=4.0
DazzleScaleY=4.0
FadeoutStart=20.0
FadeoutEnd=30.0
SizeOptimizationLimit=0.05
HistoryWeight=0.1
UseCameraTranslation=0
HaloTextureName=SunHalo.tga
DazzleTextureName=SunDazzle.tga
DazzleColor=1.0,1.0,1.0
LensflareName=SUN_LENSFLARE
;========================================== LENSFLARE DEFINITIONS =================
[SUN_LENSFLARE]
TextureName=SunLensFlare.tga
FlareCount=6
FlareLocation1=1.3
FlareLocation2=0.4
FlareLocation3=-0.1
FlareLocation4=-0.6
FlareLocation5=-0.9
FlareLocation6=-1.2
FlareSize1=0.06
FlareSize2=0.04
FlareSize3=0.03
FlareSize4=0.15
FlareSize5=0.20
FlareSize6=0.30
FlareUV1=0.000,0.000,0.118,0.238
FlareUV2=0.134,0.048,0.204,0.190
FlareUV3=0.228,0.079,0.276,0.175
FlareUV4=0.000,0.619,0.165,1.000
FlareUV5=0.181,0.365,0.488,1.000
FlareUV6=0.496,0.000,1.000,1.000

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View file

@ -0,0 +1,173 @@
State Management Responsibilities
Key:
G - Global
DX8W - DX8 Wrapper
LE - Wrapper::Set Light Environment
DX8R - DX8 Renderer (affects meshes only)
Shader - ShaderClass
VM - VertexMaterial
Map - Mapper (part of VertexMaterial)
Texture - TextureClass
WW3D
_D3DRENDERSTATETYPE {
ZENABLE = 7,
FILLMODE = 8, WW3D
SHADEMODE = 9,
LINEPATTERN = 10,
ZWRITEENABLE = 14, Shader
ALPHATESTENABLE = 15, Shader
LASTPIXEL = 16,
SRCBLEND = 19, Shader
DESTBLEND = 20, Shader
CULLMODE = 22, Shader
ZFUNC = 23, Shader
ALPHAREF = 24, Shader
ALPHAFUNC = 25, Shader
DITHERENABLE = 26, G
ALPHABLENDENABLE = 27, Shader
FOGENABLE = 28, Shader
SPECULARENABLE = 29, Shader
ZVISIBLE = 30,
FOGCOLOR = 34, Shader
FOGTABLEMODE = 35, G
FOGSTART = 36, Scene
FOGEND = 37, Scene
FOGDENSITY = 38,
EDGEANTIALIAS = 40,
ZBIAS = 47, G (set to zero after device is initted)
RANGEFOGENABLE = 48, G
STENCILENABLE = 52,
STENCILFAIL = 53,
STENCILZFAIL = 54,
STENCILPASS = 55,
STENCILFUNC = 56,
STENCILREF = 57,
STENCILMASK = 58,
STENCILWRITEMASK = 59,
TEXTUREFACTOR = 60,
WRAP0 = 128,
WRAP1 = 129,
WRAP2 = 130,
WRAP3 = 131,
WRAP4 = 132,
WRAP5 = 133,
WRAP6 = 134,
WRAP7 = 135,
CLIPPING = 136,
LIGHTING = 137, VM
AMBIENT = 139, LE
FOGVERTEXMODE = 140, G
COLORVERTEX = 141, G
LOCALVIEWER = 142,
NORMALIZENORMALS = 143,
DIFFUSEMATERIALSOURCE = 145, VM
SPECULARMATERIALSOURCE = 146, G
AMBIENTMATERIALSOURCE = 147, VM, set to same setting as diffuse material source
EMISSIVEMATERIALSOURCE = 148, VM
VERTEXBLEND = 151,
CLIPPLANEENABLE = 152,
SOFTWAREVERTEXPROCESSING = 153, G
POINTSIZE = 154,
POINTSIZE_MIN = 155,
POINTSPRITEENABLE = 156,
POINTSCALEENABLE = 157,
POINTSCALE_A = 158,
POINTSCALE_B = 159,
POINTSCALE_C = 160,
MULTISAMPLEANTIALIAS = 161,
MULTISAMPLEMASK = 162,
PATCHEDGESTYLE = 163,
PATCHSEGMENTS = 164,
DEBUGMONITORTOKEN = 165,
POINTSIZE_MAX = 166,
INDEXEDVERTEXBLENDENABLE = 167,
COLORWRITEENABLE = 168,
TWEENFACTOR = 170,
BLENDOP = 171,
FORCE_DWORD = 0x7fffffff
} D3DRENDERSTATETYPE;
_D3DTEXTURESTAGESTATETYPE {
COLOROP = 1, Shader stages 0,1 only
COLORARG1 = 2, Shader stages 0,1 only
COLORARG2 = 3, Shader stages 0,1 only
ALPHAOP = 4, Shader stages 0,1 only
ALPHAARG1 = 5, Shader stages 0,1 only
ALPHAARG2 = 6, Shader stages 0,1 only
BUMPENVMAT00 = 7,
BUMPENVMAT01 = 8,
BUMPENVMAT10 = 9,
BUMPENVMAT11 = 10,
TEXCOORDINDEX = 11, DX8R, Map
ADDRESSU = 13, Texture
ADDRESSV = 14, Texture
BORDERCOLOR = 15,
MAGFILTER = 16, Texture
MINFILTER = 17, Texture
MIPFILTER = 18, Texture
MIPMAPLODBIAS = 19,
MAXMIPLEVEL = 20,
MAXANISOTROPY = 21,
BUMPENVLSCALE = 22,
BUMPENVLOFFSET = 23,
TEXTURETRANSFORMFLAGS = 24, Map
ADDRESSW = 25,
COLORARG0 = 26,
ALPHAARG0 = 27,
RESULTARG = 28,
FORCE_DWORD = 0x7fffffff
} D3DTEXTURESTAGESTATETYPE;
Variables
_D3DTRANSFORMSTATETYPE {
D3DTS_VIEW = 2, Pointgroup (restores), Camera
D3DTS_PROJECTION = 3, Camera
D3DTS_TEXTURE0 = 16,
D3DTS_TEXTURE1 = 17,
D3DTS_TEXTURE2 = 18,
D3DTS_TEXTURE3 = 19,
D3DTS_TEXTURE4 = 20,
D3DTS_TEXTURE5 = 21,
D3DTS_TEXTURE6 = 22,
D3DTS_TEXTURE7 = 23,
D3DTS_FORCE_DWORD = 0x7fffffff
D3DTS_WORLDMATRIX(0) = box, pointgroup, camera
} D3DTRANSFORMSTATETYPE;
Clip Planes =
Clip Status =
Current Texture Palette =
Cursor Position =
Cursor Properties =
Gamma Ramp =
Indices = DX8W
Light = LE
Material = VM
Palette Entries =
Pixel Shader =
Pixel Shader Const =
Render Target =
Stream Source = DX8W
Texture = DX8W
WORLD Transform = box, DX8R, pointgr
VIEW Transform = pointgr
PROJECTION Transform = camera
TEXTURE Transform = Map
Vertex Shader = DX8W
Vertex Shader Const =
Viewport = Camera, WW3D

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,354 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/aabtree.h $*
* *
* Org Author:: Greg Hjelstrom *
* *
* Author:: Kenny Mitchell *
* *
* $Modtime:: 6/26/02 2:58p $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* 06/26/02 KM Integrating shader system
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef AABTREE_H
#define AABTREE_H
#include "always.h"
#include "refcount.h"
#include "simplevec.h"
#include "vector3.h"
#include "vector3i.h"
#include "aaplane.h"
#include "bittype.h"
#include "colmath.h"
#include "wwdebug.h"
#include "aabtreebuilder.h"
#include "obbox.h"
#include <tri.h>
#include <float.h>
class MeshClass;
class CameraClass;
class RayCollisionTestClass;
class AABoxCollisionTestClass;
class OBBoxCollisionTestClass;
class OBBoxIntersectionTestClass;
class ChunkLoadClass;
class ChunkSaveClass;
class MeshGeometryClass;
class OBBoxClass;
class ChunkLoadClass;
struct BoxRayAPTContextStruct;
#define AABTREE_LEAF_FLAG 0x80000000
/*
** AABTreeClass
** This class encapsulates an Axis-Aligned Bounding Box Tree for a mesh. This tree
** can be used to perform hierarchical culling for collision detection. Note that
** this class is constructed using the AABTreeBuilderClass; these two classes are
** very tightly coupled. Pretty much the only code which needs to know about the AABTreeClass
** is in MeshGeometryClass. I moved these out into a separate file just to reduce the
** size of meshmdl.cpp.
*/
class AABTreeClass : public W3DMPO, public RefCountClass
{
W3DMPO_GLUE(AABTreeClass)
public:
AABTreeClass(void);
AABTreeClass(AABTreeBuilderClass * builder);
AABTreeClass(const AABTreeClass & that);
~AABTreeClass(void);
void Load_W3D(ChunkLoadClass & cload);
// Uniformly scale the AABTree
void Scale(float scale);
int Get_Node_Count(void) { return NodeCount; }
int Get_Poly_Count(void) { return PolyCount; }
int Compute_Ram_Size(void);
void Generate_APT(const OBBoxClass & box,SimpleDynVecClass<uint32> & apt);
void Generate_APT(const OBBoxClass & box,const Vector3 & viewdir,SimpleDynVecClass<uint32> & apt);
bool Cast_Ray(RayCollisionTestClass & raytest);
int Cast_Semi_Infinite_Axis_Aligned_Ray(const Vector3 & start_point,
int axis_dir, unsigned char & flags);
bool Cast_AABox(AABoxCollisionTestClass & boxtest);
bool Cast_OBBox(OBBoxCollisionTestClass & boxtest);
bool Intersect_OBBox(OBBoxIntersectionTestClass & boxtest);
void Set_Mesh(MeshGeometryClass * mesh);
private:
AABTreeClass & operator = (const AABTreeClass & that);
void Read_Poly_Indices(ChunkLoadClass & cload);
void Read_Nodes(ChunkLoadClass & cload);
void Build_Tree_Recursive(AABTreeBuilderClass::CullNodeStruct * node,int &curpolyindex);
void Reset(void);
void Update_Bounding_Boxes(void);
void Update_Min_Max(int index,Vector3 & min,Vector3 & max);
/*
** CullNodeStruct - the culling tree is built out of an array of these structures
** They contain the extents of an axis-aligned box, indices to children nodes,
** and indices into the polygon array
** (05/22/2000 gth - changed this structure to support either child nodes -or-
** a polygon array but not both at the same time. Also switched to 32bit indices
** so that the code doesn't become useless as quickly )
*/
struct CullNodeStruct
{
Vector3 Min;
Vector3 Max;
uint32 FrontOrPoly0;
uint32 BackOrPolyCount;
// accessors
inline bool Is_Leaf(void);
inline int Get_Back_Child(void); // returns index of back child (only call for non-LEAFs!!!)
inline int Get_Front_Child(void); // returns index of front child (only call for non-LEAFs!!!)
inline int Get_Poly0(void); // returns index of first polygon (only call on LEAFs)
inline int Get_Poly_Count(void); // returns polygon count (only call on LEAFs)
// initialization
inline void Set_Front_Child(uint32 index);
inline void Set_Back_Child(uint32 index);
inline void Set_Poly0(uint32 index);
inline void Set_Poly_Count(uint32 count);
};
/*
** OBBoxAPTContextStruct - this is a temporary datastructure used in building
** an APT by culling the mesh to an oriented bounding box.
*/
struct OBBoxAPTContextStruct
{
OBBoxAPTContextStruct(const OBBoxClass & box,SimpleDynVecClass<uint32> & apt) :
Box(box), APT(apt)
{ }
OBBoxClass Box;
SimpleDynVecClass<uint32> & APT;
};
/**
** OBBoxRayAPTContextStruct - temporary datastructure used in building an APT
** by culling the mesh to a oriented box and eliminating backfaces to a ray.
*/
struct OBBoxRayAPTContextStruct
{
OBBoxRayAPTContextStruct(const OBBoxClass & box,const Vector3 & viewdir,SimpleDynVecClass<uint32> & apt) :
Box(box),
ViewVector(viewdir),
APT(apt)
{ }
OBBoxClass Box;
Vector3 ViewVector;
SimpleDynVecClass<uint32> & APT;
};
void Generate_OBBox_APT_Recursive(CullNodeStruct * node,OBBoxAPTContextStruct & context);
void Generate_OBBox_APT_Recursive(CullNodeStruct * node, OBBoxRayAPTContextStruct & context);
bool Cast_Ray_Recursive(CullNodeStruct * node,RayCollisionTestClass & raytest);
int Cast_Semi_Infinite_Axis_Aligned_Ray_Recursive(CullNodeStruct * node, const Vector3 & start_point,
int axis_r, int axis_1, int axis_2, int direction, unsigned char & flags);
bool Cast_AABox_Recursive(CullNodeStruct * node,AABoxCollisionTestClass & boxtest);
bool Cast_OBBox_Recursive(CullNodeStruct * node,OBBoxCollisionTestClass & boxtest);
bool Intersect_OBBox_Recursive(CullNodeStruct * node,OBBoxIntersectionTestClass & boxtest);
bool Cast_Ray_To_Polys(CullNodeStruct * node,RayCollisionTestClass & raytest);
int Cast_Semi_Infinite_Axis_Aligned_Ray_To_Polys(CullNodeStruct * node, const Vector3 & start_point,
int axis_r, int axis_1, int axis_2, int direction, unsigned char & flags);
bool Cast_AABox_To_Polys(CullNodeStruct * node,AABoxCollisionTestClass & boxtest);
bool Cast_OBBox_To_Polys(CullNodeStruct * node,OBBoxCollisionTestClass & boxtest);
bool Intersect_OBBox_With_Polys(CullNodeStruct * node,OBBoxIntersectionTestClass & boxtest);
void Update_Bounding_Boxes_Recursive(CullNodeStruct * node);
int NodeCount; // number of nodes in the tree
CullNodeStruct * Nodes; // array of nodes
int PolyCount; // number of polygons in the parent mesh (and the number of indexes in our array)
uint32 * PolyIndices; // linear array of polygon indices, nodes index into this array
MeshGeometryClass * Mesh; // pointer to the parent mesh (non-ref-counted; we are a member of this mesh)
friend class MeshClass;
friend class MeshGeometryClass;
friend class AuxMeshDataClass;
friend class AABTreeBuilderClass;
};
inline int AABTreeClass::Compute_Ram_Size(void)
{
return NodeCount * sizeof(CullNodeStruct) +
PolyCount * sizeof(int) +
sizeof(AABTreeClass);
}
inline bool AABTreeClass::Cast_Ray(RayCollisionTestClass & raytest)
{
WWASSERT(Nodes != NULL);
return Cast_Ray_Recursive(&(Nodes[0]),raytest);
}
inline int AABTreeClass::Cast_Semi_Infinite_Axis_Aligned_Ray(const Vector3 & start_point,
int axis_dir, unsigned char & flags)
{
// These tables translate between the axis_dir representation (which is an integer in which 0
// indicates a ray along the positive x axis, 1 along the negative x axis, 2 the positive y
// axis, 3 negative y axis, 4 positive z axis, 5 negative z axis) and a four-integer
// representation (axis_r is the axis number - 0, 1 or 2 - of the axis along which the ray is
// cast; axis_1 and axis_2 are the axis numbers of the other two axes; direction is 0 for
// negative and 1 for positive direction of the ray).
static const int axis_r[6] = { 0, 0, 1, 1, 2, 2 };
static const int axis_1[6] = { 1, 1, 2, 2, 0, 0 };
static const int axis_2[6] = { 2, 2, 0, 0, 1, 1 };
static const int direction[6] = { 1, 0, 1, 0, 1, 0 };
WWASSERT(Nodes != NULL);
WWASSERT(axis_dir >= 0);
WWASSERT(axis_dir < 6);
// The functions called after this point will 'or' bits into this variable, so it needs to
// be initialized here to TRI_RAYCAST_FLAG_NONE.
flags = TRI_RAYCAST_FLAG_NONE;
return Cast_Semi_Infinite_Axis_Aligned_Ray_Recursive(&(Nodes[0]), start_point,
axis_r[axis_dir], axis_1[axis_dir], axis_2[axis_dir], direction[axis_dir], flags);
}
inline bool AABTreeClass::Cast_AABox(AABoxCollisionTestClass & boxtest)
{
WWASSERT(Nodes != NULL);
return Cast_AABox_Recursive(&(Nodes[0]),boxtest);
}
inline bool AABTreeClass::Cast_OBBox(OBBoxCollisionTestClass & boxtest)
{
WWASSERT(Nodes != NULL);
return Cast_OBBox_Recursive(&(Nodes[0]),boxtest);
}
inline bool AABTreeClass::Intersect_OBBox(OBBoxIntersectionTestClass & boxtest)
{
WWASSERT(Nodes != NULL);
return Intersect_OBBox_Recursive(&(Nodes[0]),boxtest);
}
inline void AABTreeClass::Update_Bounding_Boxes(void)
{
WWASSERT(Nodes != NULL);
Update_Bounding_Boxes_Recursive(&(Nodes[0]));
}
/***********************************************************************************************
AABTreeClass::CullNodeStruct implementation
These nodes can be either leaf nodes or non-leaf nodes. If they are leaf nodes, they
will contain an index to their first polygon index and a polygon count. If they are
non-leafs they will contain indices to their front and back children. Since I'm re-using
the same variables for the child indices and the polygon indices, you have to call
the Is_Leaf function then only call the appropriate functions. The flag indicating whether
this node is a leaf is stored in the MSB of the FrontOrPoly0 variable. It will always
be stripped off by these accessor functions
***********************************************************************************************/
inline bool AABTreeClass::CullNodeStruct::Is_Leaf(void)
{
return ((FrontOrPoly0 & AABTREE_LEAF_FLAG) != 0);
}
inline int AABTreeClass::CullNodeStruct::Get_Front_Child(void)
{
WWASSERT(!Is_Leaf());
return FrontOrPoly0; // we shouldn't be calling this on a leaf and the leaf bit should be zero...
}
inline int AABTreeClass::CullNodeStruct::Get_Back_Child(void)
{
WWASSERT(!Is_Leaf());
return BackOrPolyCount;
}
inline int AABTreeClass::CullNodeStruct::Get_Poly0(void)
{
WWASSERT(Is_Leaf());
return (FrontOrPoly0 & ~AABTREE_LEAF_FLAG);
}
inline int AABTreeClass::CullNodeStruct::Get_Poly_Count(void)
{
WWASSERT(Is_Leaf());
return BackOrPolyCount;
}
inline void AABTreeClass::CullNodeStruct::Set_Front_Child(uint32 index)
{
WWASSERT(index < 0x7FFFFFFF);
FrontOrPoly0 = index;
}
inline void AABTreeClass::CullNodeStruct::Set_Back_Child(uint32 index)
{
WWASSERT(index < 0x7FFFFFFF);
BackOrPolyCount = index;
}
inline void AABTreeClass::CullNodeStruct::Set_Poly0(uint32 index)
{
WWASSERT(index < 0x7FFFFFFF);
FrontOrPoly0 = (index | AABTREE_LEAF_FLAG);
}
inline void AABTreeClass::CullNodeStruct::Set_Poly_Count(uint32 count)
{
WWASSERT(count < 0x7FFFFFFF);
BackOrPolyCount = count;
}
#endif

View file

@ -0,0 +1,998 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/aabtreebuilder.cpp $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 11/24/01 5:49p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* AABTreeBuilderClass::AABTreeBuilderClass -- Constructor *
* AABTreeBuilderClass::~AABTreeBuilderClass -- Destructor *
* AABTreeBuilderClass::Reset -- reset the builder, delete all arrays *
* AABTreeBuilderClass::Build_AABTree -- Build an AABTree for the given mesh. *
* AABTreeBuilderClass::Build_Tree -- recursivly builds the culling tree *
* AABTreeBuilderClass::Select_Splitting_Plane -- select a partition for the given polys *
* AABTreeBuilderClass::Compute_Plane_Score -- evaluate the suitability of a partition plane *
* AABTreeBuilderClass::Which_Side -- which side of a plane is the given poly *
* AABTreeBuilderClass::Split_Polys -- partition the polys with a plane *
* AABTreeBuilderClass::Compute_Bounding_Box -- compute bounding boxes for the cull nodes *
* AABTreeBuilderClass::Assign_Index -- assign an array index to each node *
* AABTreeBuilderClass::Node_Count -- Count the nodes in the tree *
* AABTreeBuilderClass::Poly_Count -- returns number of polys *
* AABTreeBuilderClass::Node_Count_Recursive -- internal implementation of Node_Count *
* AABTreeBuilderClass::Submit_Tree -- install nodes into an AABTreeClass *
* AABTreeBuilderClass::Submit_Tree_Recursive -- internal implementation of Submit_Tree *
* AABTreeBuilderClass::Update_Min -- ensure given vector is < min of the poly *
* AABTreeBuilderClass::Update_Max -- ensure given vector is > max of poly *
* AABTreeBuilderClass::Update_Min_Max -- ensure given vector is in min max of poly *
* AABTreeBuilderClass::Export -- Saves this AABTree into a W3D chunk *
* AABTreeBuilderClass::Build_W3D_AABTree_Recursive -- Build array of indices and W3dMeshAAB *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "aabtreebuilder.h"
#include "chunkio.h"
#include "w3d_file.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#undef WWASSERT
#define WWASSERT assert // can't use WWASSERT because we use this module in the MAX plugin...
const float COINCIDENCE_EPSILON = 0.001f;
/***********************************************************************************************
* AABTreeBuilderClass::AABTreeBuilderClass -- Constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
*=============================================================================================*/
AABTreeBuilderClass::AABTreeBuilderClass(void) :
Root(NULL),
CurPolyIndex(0),
PolyCount(0),
Polys(NULL),
VertCount(0),
Verts(NULL)
{
}
/***********************************************************************************************
* AABTreeBuilderClass::~AABTreeBuilderClass -- Destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/19/2000 gth : Created. *
*=============================================================================================*/
AABTreeBuilderClass::~AABTreeBuilderClass(void)
{
Reset();
}
/***********************************************************************************************
* AABTreeBuilderClass::Reset -- reset the builder, delete all arrays *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/19/2000 gth : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Reset(void)
{
if (Root) {
delete Root; Root = NULL;
}
if (Verts != NULL) {
delete[] Verts;
Verts = NULL;
}
if (Polys != NULL) {
delete[] Polys;
Polys = NULL;
}
}
/***********************************************************************************************
* AABTreeBuilderClass::Build_AABTree -- Build an AABTree for the given mesh. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Build_AABTree(int polycount,TriIndex * polys,int vertcount,Vector3 * verts)
{
WWASSERT(polycount > 0);
WWASSERT(vertcount > 0);
WWASSERT(polys != NULL);
WWASSERT(verts != NULL);
/*
** If we already have allocated data, release it
*/
Reset();
/*
** Copy the mesh data
*/
VertCount = vertcount;
PolyCount = polycount;
Verts = W3DNEWARRAY Vector3[VertCount];
Polys = W3DNEWARRAY TriIndex[PolyCount];
for (int vi=0; vi<VertCount; vi++) {
Verts[vi] = verts[vi];
}
for (int pi=0; pi<PolyCount; pi++) {
Polys[pi] = polys[pi];
}
/*
** First, create a list of all of the poly indices
*/
int * polyindices = W3DNEWARRAY int[PolyCount];
for (int i=0; i<PolyCount; i++) {
polyindices[i] = i;
}
/*
** Build the tree, note that the array of poly indices will be
** deleted by the Build_Tree function.
*/
Root = W3DNEW CullNodeStruct;
Build_Tree(Root,PolyCount,polyindices);
polyindices = NULL;
/*
** fill in the remaining information needed in the tree:
** for example: bounding boxes, index assignments
*/
Compute_Bounding_Box(Root);
Assign_Index(Root,0);
}
/***********************************************************************************************
* AABTreeBuilderClass::Build_AABTree -- Build an AABTree for the given mesh. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
* 6/28/02 KJM : This version handles 32-bit indexes
*=============================================================================================*/
void AABTreeBuilderClass::Build_AABTree(int polycount,Vector3i * polys,int vertcount,Vector3 * verts)
{
WWASSERT(polycount > 0);
WWASSERT(vertcount > 0);
WWASSERT(polys != NULL);
WWASSERT(verts != NULL);
/*
** If we already have allocated data, release it
*/
Reset();
/*
** Copy the mesh data
*/
VertCount = vertcount;
PolyCount = polycount;
Verts = new Vector3[VertCount];
Polys = new TriIndex[PolyCount];
for (int vi=0; vi<VertCount; vi++) {
Verts[vi] = verts[vi];
}
for (int pi=0; pi<PolyCount; pi++) {
Polys[pi].I = polys[pi].I;
Polys[pi].J = polys[pi].J;
Polys[pi].K = polys[pi].K;
}
/*
** First, create a list of all of the poly indices
*/
int * polyindices = new int[PolyCount];
for (int i=0; i<PolyCount; i++) {
polyindices[i] = i;
}
/*
** Build the tree, note that the array of poly indices will be
** deleted by the Build_Tree function.
*/
Root = new CullNodeStruct;
Build_Tree(Root,PolyCount,polyindices);
polyindices = NULL;
/*
** fill in the remaining information needed in the tree:
** for example: bounding boxes, index assignments
*/
Compute_Bounding_Box(Root);
Assign_Index(Root,0);
}
/***********************************************************************************************
* AABTreeBuilderClass::Build_Tree -- recursivly builds the culling tree *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Build_Tree(CullNodeStruct * node,int polycount,int * polyindices)
{
/*
** First, if there are only a few polys left, just terminate the tree
*/
if (polycount <= MIN_POLYS_PER_NODE) {
node->PolyCount = polycount;
node->PolyIndices = polyindices;
return;
}
/*
** Try to find a suitable partitioning plane.
*/
SplitChoiceStruct sc;
sc = Select_Splitting_Plane(polycount,polyindices);
/*
** If the algorithm could not separate any polys, just install the polys
** in this node and terminate. TODO: explore how this happens.
*/
if (sc.FrontCount + sc.BackCount != polycount) {
node->PolyCount = polycount;
node->PolyIndices = polyindices;
return;
}
/*
** Decide whether to actually partition this node. If the partitioning
** will not gain us anything, just install the polys in this node and terminate
** the tree.
*/
#if 0
if (sc.Cost == MAX_COST) {
node->PolyCount = polycount;
node->PolyIndices = polyindices;
return;
}
#endif
/*
** Ok, split the polys
*/
SplitArraysStruct arrays;
Split_Polys(polycount,polyindices,sc,&arrays);
/*
** Free the memory in use by the input tile-list
*/
delete[] polyindices;
/*
** Build a front tree if necessary. Remember that the Build function
** deletes the poly array.
*/
if (arrays.FrontCount) {
WWASSERT(arrays.FrontPolys != NULL);
node->Front = W3DNEW CullNodeStruct;
Build_Tree(node->Front,arrays.FrontCount,arrays.FrontPolys);
arrays.FrontPolys = NULL;
}
/*
** Build a back tree if necessary. Remember that the build function
** deletes the tile array.
*/
if (arrays.BackCount) {
WWASSERT(arrays.BackPolys != NULL);
node->Back = W3DNEW CullNodeStruct;
Build_Tree(node->Back,arrays.BackCount,arrays.BackPolys);
arrays.BackPolys = NULL;
}
}
/***********************************************************************************************
* AABTreeBuilderClass::Select_Splitting_Plane -- select a partition for the given polys *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
AABTreeBuilderClass::SplitChoiceStruct
AABTreeBuilderClass::Select_Splitting_Plane(int polycount,int * polyindices)
{
WWASSERT(polyindices != NULL);
const int NUM_TRYS = 50;
SplitChoiceStruct best_plane_stats;
SplitChoiceStruct considered_plane_stats;
/*
** Try putting axis-aligned planes through some random vertices
*/
for (int trys = 0; trys < MIN(NUM_TRYS,polycount); trys++) {
AAPlaneClass plane;
/*
** Select a random poly and vertex index;
*/
int poly_index = polyindices[rand() % polycount];
int vert_index = rand() % 3;
const TriIndex * polyverts = Polys + poly_index;
const Vector3 * vert = Verts + (*polyverts)[vert_index];
/*
** Select a random plane
*/
switch(rand() % 3) {
case 0: plane.Set(AAPlaneClass::XNORMAL,vert->X); break;
case 1: plane.Set(AAPlaneClass::YNORMAL,vert->Y); break;
case 2: plane.Set(AAPlaneClass::ZNORMAL,vert->Z); break;
};
/*
** Get the score for this plane
*/
considered_plane_stats = Compute_Plane_Score(polycount,polyindices,plane);
if (considered_plane_stats.Cost < best_plane_stats.Cost) {
best_plane_stats = considered_plane_stats;
}
}
return best_plane_stats;
}
/***********************************************************************************************
* AABTreeBuilderClass::Compute_Plane_Score -- evaluate the suitability of a partition plane *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
AABTreeBuilderClass::SplitChoiceStruct
AABTreeBuilderClass::Compute_Plane_Score(int polycount,int * polyindices,const AAPlaneClass & plane)
{
/*
** The score of a splitting plane is based on the following factors:
** - the volumes of the resulting two children volumes,
** - the number of polys in each child volume
*/
SplitChoiceStruct sc;
sc.Plane = plane;
for (int i=0; i<polycount; i++) {
switch(Which_Side(plane,polyindices[i])) {
case FRONT:
case ON:
case BOTH:
{
sc.FrontCount++;
Update_Min_Max(polyindices[i],sc.FMin,sc.FMax );
break;
}
case BACK:
{
sc.BackCount++;
Update_Min_Max(polyindices[i],sc.BMin,sc.BMax );
break;
}
}
}
/*
** Inflate the box a tiny amount so that we never
** get volumes of zero!
*/
sc.BMin -= Vector3(WWMATH_EPSILON,WWMATH_EPSILON,WWMATH_EPSILON);
sc.BMax += Vector3(WWMATH_EPSILON,WWMATH_EPSILON,WWMATH_EPSILON);
/*
** Compute the cost.
*/
float back_cost = (sc.BMax.X - sc.BMin.X) * (sc.BMax.Y - sc.BMin.Y) * (sc.BMax.Z - sc.BMin.Z) * sc.BackCount;
float front_cost = (sc.FMax.X - sc.FMin.X) * (sc.FMax.Y - sc.FMin.Y) * (sc.FMax.Z - sc.FMin.Z) * sc.FrontCount;
sc.Cost = front_cost + back_cost;
if ((sc.FrontCount == 0) || (sc.BackCount == 0)) {
sc.Cost = FLT_MAX;
}
return sc;
}
/***********************************************************************************************
* AABTreeBuilderClass::Which_Side -- which side of a plane is the given poly *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
AABTreeBuilderClass::OverlapType
AABTreeBuilderClass::Which_Side(const AAPlaneClass & plane,int poly_index)
{
/*
** Check each vertex to see if it is in front, behind or on the plane
*/
int mask = 0;
for (int vi=0; vi<3; vi++) {
const Vector3 & point = Verts[ Polys[poly_index][vi] ];
float delta = point[plane.Normal] - plane.Dist;
if (delta > COINCIDENCE_EPSILON) {
mask |= POS;
}
if (delta < -COINCIDENCE_EPSILON) {
mask |= NEG;
}
mask |= ON;
}
/*
** Now evaluate the status of all of the verts to determine whether the
** triangle is in front, behind, on or overlapping the plane
*/
/*
** If all verts were ON the plane, the triangle is ON the plane
*/
if (mask == ON) {
return ON;
}
/*
** If all verts were POS or ON, the triangle is POS (IN_FRONT)
*/
if ((mask & ~(POS | ON)) == 0) {
return POS;
}
/*
** If all verts were NEG or ON, the triangle is NEG (BEHIND)
*/
if ((mask & ~(NEG | ON)) == 0) {
return NEG;
}
/*
** Otherwise, the triangle spans the plane
*/
return BOTH;
}
/***********************************************************************************************
* AABTreeBuilderClass::Split_Polys -- partition the polys with a plane *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Split_Polys
(
int polycount,
int * polyindices,
const SplitChoiceStruct & sc,
SplitArraysStruct * arrays
)
{
/*
** Note that this routine arrays of polygons. The caller is then responsible for keeping
** track of the memory this routine allocates.
*/
if (sc.FrontCount > 0) {
arrays->FrontPolys = W3DNEWARRAY int[sc.FrontCount];
}
if (sc.BackCount > 0) {
arrays->BackPolys = W3DNEWARRAY int[sc.BackCount];
}
arrays->FrontCount = 0;
arrays->BackCount = 0;
for (int i=0; i<polycount; i++) {
switch(Which_Side(sc.Plane,polyindices[i])) {
case FRONT:
case ON:
case BOTH:
arrays->FrontPolys[arrays->FrontCount++] = polyindices[i];
break;
case BACK:
arrays->BackPolys[arrays->BackCount++] = polyindices[i];
break;
}
}
/*
** when we are all done, the counts should match.
*/
WWASSERT(arrays->FrontCount == sc.FrontCount);
WWASSERT(arrays->BackCount == sc.BackCount);
}
/***********************************************************************************************
* AABTreeBuilderClass::Compute_Bounding_Box -- compute bounding boxes for the cull nodes *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Compute_Bounding_Box(CullNodeStruct * node)
{
/*
** compute bounding volumes of the children
*/
if (node->Front) {
Compute_Bounding_Box(node->Front);
}
if (node->Back) {
Compute_Bounding_Box(node->Back);
}
/*
** compute bounding volume for the polys in this node
*/
const float really_big=WWMATH_FLOAT_MAX;
node->Min.Set(really_big,really_big,really_big);
node->Max.Set(-really_big,-really_big,-really_big);
for (int poly_index = 0; poly_index < node->PolyCount; poly_index++) {
Update_Min_Max(node->PolyIndices[poly_index],node->Min,node->Max );
}
/*
** bound the polys in the front child node
*/
if (node->Front) {
if (node->Front->Min.X < node->Min.X) node->Min.X = node->Front->Min.X;
if (node->Front->Max.X > node->Max.X) node->Max.X = node->Front->Max.X;
if (node->Front->Min.Y < node->Min.Y) node->Min.Y = node->Front->Min.Y;
if (node->Front->Max.Y > node->Max.Y) node->Max.Y = node->Front->Max.Y;
if (node->Front->Min.Z < node->Min.Z) node->Min.Z = node->Front->Min.Z;
if (node->Front->Max.Z > node->Max.Z) node->Max.Z = node->Front->Max.Z;
}
/*
** bound the polys in the back child node
*/
if (node->Back) {
if (node->Back->Min.X < node->Min.X) node->Min.X = node->Back->Min.X;
if (node->Back->Max.X > node->Max.X) node->Max.X = node->Back->Max.X;
if (node->Back->Min.Y < node->Min.Y) node->Min.Y = node->Back->Min.Y;
if (node->Back->Max.Y > node->Max.Y) node->Max.Y = node->Back->Max.Y;
if (node->Back->Min.Z < node->Min.Z) node->Min.Z = node->Back->Min.Z;
if (node->Back->Max.Z > node->Max.Z) node->Max.Z = node->Back->Max.Z;
}
WWASSERT(node->Min.X != really_big);
WWASSERT(node->Min.Y != really_big);
WWASSERT(node->Min.Z != really_big);
WWASSERT(node->Max.X != -really_big);
WWASSERT(node->Max.Y != -really_big);
WWASSERT(node->Max.Z != -really_big);
}
/***********************************************************************************************
* AABTreeBuilderClass::Assign_Index -- assign an array index to each node *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
int AABTreeBuilderClass::Assign_Index(CullNodeStruct * node,int index)
{
/*
** This function is used to assign a sequential index to
** each node in the tree. The AABTree stores its nodes in
** an array so this index is used to determine which slot
** in the array to put each node into.
*/
WWASSERT(node);
node->Index = index;
index++;
if (node->Front) {
index = Assign_Index(node->Front,index);
}
if (node->Back) {
index = Assign_Index(node->Back,index);
}
return index;
}
/***********************************************************************************************
* AABTreeBuilderClass::Node_Count -- Count the nodes in the tree *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
int AABTreeBuilderClass::Node_Count(void)
{
if (Root) {
return Node_Count_Recursive(Root,0);
} else {
return 0;
}
}
/***********************************************************************************************
* AABTreeBuilderClass::Poly_Count -- returns number of polys *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/23/98 GTH : Created. *
*=============================================================================================*/
int AABTreeBuilderClass::Poly_Count(void)
{
return PolyCount;
}
/***********************************************************************************************
* AABTreeBuilderClass::Node_Count_Recursive -- internal implementation of Node_Count *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/19/98 GTH : Created. *
*=============================================================================================*/
int AABTreeBuilderClass::Node_Count_Recursive(CullNodeStruct * node,int curcount)
{
curcount++;
if (node->Front) {
curcount = Node_Count_Recursive(node->Front,curcount);
}
if (node->Back) {
curcount = Node_Count_Recursive(node->Back,curcount);
}
return curcount;
}
/***********************************************************************************************
* AABTreeBuilderClass::Update_Min -- ensure given vector is < min of the poly *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/22/98 GTH : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Update_Min(int poly_index,Vector3 & min)
{
for (int vert_index = 0; vert_index < 3; vert_index++) {
const TriIndex * polyverts = Polys + poly_index;
const Vector3 * point = Verts + (*polyverts)[vert_index];
if (point->X < min.X) min.X = point->X;
if (point->Y < min.Y) min.Y = point->Y;
if (point->Z < min.Z) min.Z = point->Z;
}
}
/***********************************************************************************************
* AABTreeBuilderClass::Update_Max -- ensure given vector is > max of poly *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 6/22/98 GTH : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Update_Max(int poly_index,Vector3 & max)
{
for (int vert_index = 0; vert_index < 3; vert_index++) {
const TriIndex * polyverts = Polys + poly_index;
const Vector3 * point = Verts + (*polyverts)[vert_index];
if (point->X > max.X) max.X = point->X;
if (point->Y > max.Y) max.Y = point->Y;
if (point->Z > max.Z) max.Z = point->Z;
}
}
/***********************************************************************************************
* AABTreeBuilderClass::Update_Min_Max -- ensure given vector is in min max of poly *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/24/98 BMG : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Update_Min_Max(int poly_index, Vector3 & min, Vector3 & max)
{
for (int vert_index = 0; vert_index < 3; vert_index++) {
const TriIndex * polyverts = Polys + poly_index;
const Vector3 * point = Verts + (*polyverts)[vert_index];
if (point->X < min.X) min.X = point->X;
if (point->Y < min.Y) min.Y = point->Y;
if (point->Z < min.Z) min.Z = point->Z;
if (point->X > max.X) max.X = point->X;
if (point->Y > max.Y) max.Y = point->Y;
if (point->Z > max.Z) max.Z = point->Z;
}
}
/***********************************************************************************************
* AABTreeBuilderClass::Export -- Saves this AABTree into a W3D chunk *
* *
* This function will export the AABTree into a W3D chunk so that it can be loaded by its *
* sister class "AABTreeClass" in the WW3D library. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/22/2000 gth : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Export(ChunkSaveClass & csave)
{
csave.Begin_Chunk(W3D_CHUNK_AABTREE);
/*
** Pack the tree into an array of W3dMeshAABTreeNode's and polygon indices
*/
W3dMeshAABTreeNode * nodes = W3DNEWARRAY W3dMeshAABTreeNode[Node_Count()];
uint32 * poly_indices = W3DNEWARRAY uint32[Poly_Count()];
int cur_node = 0;
int cur_poly = 0;
Build_W3D_AABTree_Recursive(Root,nodes,poly_indices,cur_node,cur_poly);
/*
** Write out the header
*/
csave.Begin_Chunk(W3D_CHUNK_AABTREE_HEADER);
W3dMeshAABTreeHeader header;
memset(&header,0,sizeof(header));
header.NodeCount = Node_Count();
header.PolyCount = Poly_Count();
csave.Write(&header,sizeof(header));
csave.End_Chunk();
/*
** Write out the array of polygon indices
*/
csave.Begin_Chunk(W3D_CHUNK_AABTREE_POLYINDICES);
csave.Write(poly_indices,Poly_Count() * sizeof(uint32));
csave.End_Chunk();
/*
** Write out the array of nodes
*/
csave.Begin_Chunk(W3D_CHUNK_AABTREE_NODES);
for (int ni=0; ni<Node_Count(); ni++) {
csave.Write(&(nodes[ni]),sizeof(W3dMeshAABTreeNode));
}
csave.End_Chunk();
csave.End_Chunk(); // W3D_CHUNK_AABTREE done
}
/***********************************************************************************************
* AABTreeBuilderClass::Build_W3D_AABTree_Recursive -- Build array of indices and W3dMeshAABTr *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/22/2000 gth : Created. *
*=============================================================================================*/
void AABTreeBuilderClass::Build_W3D_AABTree_Recursive
(
AABTreeBuilderClass::CullNodeStruct * node,
W3dMeshAABTreeNode * w3d_nodes,
uint32 * poly_indices,
int & cur_node,
int & cur_poly
)
{
/*
** Copy data from the builder's node into our node
*/
W3dMeshAABTreeNode * newnode = &(w3d_nodes[node->Index]);
newnode->Min.X = node->Min.X;
newnode->Min.Y = node->Min.Y;
newnode->Min.Z = node->Min.Z;
newnode->Max.X = node->Max.X;
newnode->Max.Y = node->Max.Y;
newnode->Max.Z = node->Max.Z;
/*
** If this is a non-leaf node, set up the child indices, otherwise set up the polygon indices
*/
if (node->Front != NULL) {
WWASSERT(node->Back != NULL); // if we have one child, we better have both!
newnode->FrontOrPoly0 = node->Front->Index;
newnode->BackOrPolyCount = node->Back->Index;
} else {
newnode->FrontOrPoly0 = cur_poly | 0x80000000;
newnode->BackOrPolyCount = node->PolyCount;
}
/*
** Copy the polygon indices for this node into our array
*/
for (int pcounter = 0; pcounter < node->PolyCount; pcounter++) {
poly_indices[cur_poly++] = node->PolyIndices[pcounter];
}
/*
** Install the children
*/
if (node->Front) {
Build_W3D_AABTree_Recursive(node->Front,w3d_nodes,poly_indices,cur_node,cur_poly);
}
if (node->Back) {
Build_W3D_AABTree_Recursive(node->Back,w3d_nodes,poly_indices,cur_node,cur_poly);
}
}

View file

@ -0,0 +1,211 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/aabtreebuilder.h $*
* *
* Original Author:: Greg Hjelstrom *
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 11/24/01 5:49p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef AABTREEBUILDER_H
#define AABTREEBUILDER_H
#include "always.h"
#include "vector3.h"
#include "vector3i.h"
#include "aaplane.h"
#include "bittype.h"
#include "meshgeometry.h"
#include <float.h>
class AABTreeClass;
class ChunkSaveClass;
struct W3dMeshAABTreeNode;
/*
** AABTreeBuilderClass
** This class serves simply to build AABTreeClasses. It first builds a tree
** which uses an easier to manage data structure (but uses more memory). Then
** the tree is converted into the representation used in the AABTreeClass.
*/
class AABTreeBuilderClass
{
public:
AABTreeBuilderClass(void);
~AABTreeBuilderClass(void);
void Build_AABTree(int polycount,TriIndex * polys,int vertcount,Vector3 * verts);
void Build_AABTree(int polycount,Vector3i* polys,int vertcount,Vector3 * verts);
void Export(ChunkSaveClass & csave);
int Node_Count(void);
int Poly_Count(void);
enum
{
MIN_POLYS_PER_NODE = 4,
SMALL_VERTEX = -100000,
BIG_VERTEX = 100000
};
private:
/*
** This CullNodeStruct is used in building the AABTree. It is much more
** wasteful in terms of memory footprint and number of allocations than the
** streamlined version found in the actual AABTreeClass.
*/
struct CullNodeStruct
{
CullNodeStruct(void) : Index(0),Min(0,0,0),Max(0,0,0),Front(NULL),Back(NULL),PolyCount(0),PolyIndices(NULL) {}
~CullNodeStruct(void)
{
if (Front) { delete Front; }
if (Back) { delete Back; }
if (PolyIndices) { delete[] PolyIndices; }
}
int Index;
Vector3 Min;
Vector3 Max;
CullNodeStruct * Front;
CullNodeStruct * Back;
int PolyCount;
int * PolyIndices;
};
/*
** SplitChoiceStruct - encapsulates the results of evaluating the suitability of a partition
*/
struct SplitChoiceStruct
{
SplitChoiceStruct(void) :
Cost(FLT_MAX),
FrontCount(0),
BackCount(0),
BMin(BIG_VERTEX,BIG_VERTEX,BIG_VERTEX),
BMax(SMALL_VERTEX,SMALL_VERTEX,SMALL_VERTEX),
FMin(BIG_VERTEX,BIG_VERTEX,BIG_VERTEX),
FMax(SMALL_VERTEX,SMALL_VERTEX,SMALL_VERTEX),
Plane(AAPlaneClass::XNORMAL,0)
{
}
float Cost; // try to minimize this!
int FrontCount; // number of polys in front of the plane
int BackCount; // number of polys behind the plane
Vector3 BMin; // min of the bounding box of the "back" child
Vector3 BMax; // max of the bounding box of the "back" child
Vector3 FMin; // min of the bounding box of the "front" child
Vector3 FMax; // max of the bounding box of the "front" child
AAPlaneClass Plane; // partitioning plane
};
struct SplitArraysStruct
{
SplitArraysStruct(void) :
FrontCount(0),
BackCount(0),
FrontPolys(NULL),
BackPolys(NULL)
{
}
int FrontCount;
int BackCount;
int * FrontPolys;
int * BackPolys;
};
enum OverlapType
{
POS = 0x01,
NEG = 0x02,
ON = 0x04,
BOTH = 0x08,
OUTSIDE = POS,
INSIDE = NEG,
OVERLAPPED = BOTH,
FRONT = POS,
BACK = NEG,
};
/*
** Internal functions
*/
void Reset();
void Build_Tree(CullNodeStruct * node,int polycount,int * polyindices);
SplitChoiceStruct Select_Splitting_Plane(int polycount,int * polyindices);
SplitChoiceStruct Compute_Plane_Score(int polycont,int * polyindices,const AAPlaneClass & plane);
void Split_Polys(int polycount,int * polyindices,const SplitChoiceStruct & sc,SplitArraysStruct * arrays);
OverlapType Which_Side(const AAPlaneClass & plane,int poly_index);
void Compute_Bounding_Box(CullNodeStruct * node);
int Assign_Index(CullNodeStruct * node,int index);
int Node_Count_Recursive(CullNodeStruct * node,int curcount);
void Update_Min(int poly_index,Vector3 & set_min);
void Update_Max(int poly_index,Vector3 & set_max);
void Update_Min_Max(int poly_index, Vector3 & set_min, Vector3 & set_max);
void Build_W3D_AABTree_Recursive(CullNodeStruct * node,
W3dMeshAABTreeNode * w3dnodes,
uint32 * poly_indices,
int & cur_node,
int & cur_poly);
/*
** Tree
*/
CullNodeStruct * Root;
int CurPolyIndex;
/*
** Mesh data
*/
int PolyCount;
TriIndex * Polys;
int VertCount;
Vector3 * Verts;
friend class AABTreeClass;
};
#endif //AABTREEBUILDER_H

View file

@ -0,0 +1,894 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/agg_def.cpp $*
* *
* Author:: Patrick Smith
* *
* $Modtime:: 4/05/01 10:21a $*
* *
* $Revision:: 5 $*
* *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "agg_def.h"
#include "htree.h"
#include "w3derr.h"
#include "chunkio.h"
#include "wwdebug.h"
#include "assetmgr.h"
#include "matinfo.h"
#include "texture.h"
#include "wwstring.h"
#include <windows.h>
///////////////////////////////////////////////////////////////////////////////////
//
// Local constants
//
const char * const EMPTY_STRING = "";
///////////////////////////////////////////////////////////////////////////////////
//
// Global variable initialization
//
AggregateLoaderClass _AggregateLoader;
///////////////////////////////////////////////////////////////////////////////////
//
// AggregateDefClass
//
AggregateDefClass::AggregateDefClass (void)
: m_pName (NULL)
{
// Set our member data to default settings
::memset (&m_Info, 0, sizeof (m_Info));
::memset (&m_MiscInfo, 0, sizeof (m_MiscInfo));
m_MiscInfo.OriginalClassID = RenderObjClass::CLASSID_HLOD;
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// AggregateDefClass
//
AggregateDefClass::AggregateDefClass (const AggregateDefClass &src)
: m_pName (NULL)
{
// Set our member data to default settings
::memset (&m_Info, 0, sizeof (m_Info));
::memset (&m_MiscInfo, 0, sizeof (m_MiscInfo));
m_MiscInfo.OriginalClassID = RenderObjClass::CLASSID_HLOD;
// Invoke the assignment operator
(*this) = src;
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// AggregateDefClass
//
AggregateDefClass::AggregateDefClass (RenderObjClass &base_model)
: m_pName (NULL)
{
// Set our member data to default settings
::memset (&m_Info, 0, sizeof (m_Info));
::memset (&m_MiscInfo, 0, sizeof (m_MiscInfo));
m_MiscInfo.OriginalClassID = RenderObjClass::CLASSID_HLOD;
Initialize (base_model);
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// ~AggregateDefClass
//
AggregateDefClass::~AggregateDefClass (void)
{
// Free the name buffer if necessary
if (m_pName != NULL) {
// free() is used because the buffer was allocated with ::_strdup().
::free (m_pName);
m_pName = NULL;
}
Free_Subobject_List ();
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// operator=
//
const AggregateDefClass &
AggregateDefClass::operator= (const AggregateDefClass &src)
{
int index;
// Free the name buffer if necessary
if (m_pName != NULL) {
::free (m_pName);
m_pName = NULL;
}
// Start with a fresh set of data
Free_Subobject_List ();
// Copy the src object's name and info struct
Set_Name (src.Get_Name ());
::memcpy (&m_Info, &src.m_Info, sizeof (m_Info));
::memcpy (&m_MiscInfo, &src.m_MiscInfo, sizeof (m_MiscInfo));
m_Version = src.m_Version;
// Loop through all the entries in the src object's subobj list
for (index = 0; index < src.m_SubobjectList.Count (); index ++) {
W3dAggregateSubobjectStruct *pinfo = src.m_SubobjectList[index];
if (pinfo != NULL) {
// Copy the src object's info for this subobj
W3dAggregateSubobjectStruct *new_info = W3DNEW W3dAggregateSubobjectStruct;
::memcpy (new_info, pinfo, sizeof (W3dAggregateSubobjectStruct));
// Add this subobj to our list
m_SubobjectList.Add (new_info);
}
}
// Return a reference to ourselves
return *this;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Free_Subobject_List
//
void
AggregateDefClass::Free_Subobject_List (void)
{
// Delete all the stucture pointers contained in the subobject list
for (int index = 0; index < m_SubobjectList.Count (); index ++) {
W3dAggregateSubobjectStruct *pinfo = m_SubobjectList[index];
if (pinfo) {
delete pinfo;
}
}
// Reset the lists contents
m_SubobjectList.Delete_All ();
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Create
//
RenderObjClass *
AggregateDefClass::Create (void)
{
// Attempt to create an instance of the hierarchy
RenderObjClass *pmodel = Create_Render_Object (m_Info.BaseModelName);
if (pmodel != NULL) {
// Perform the aggregation
Attach_Subobjects (*pmodel);
// Let the new object know what its new name and base name are.
pmodel->Set_Name (m_pName);
pmodel->Set_Base_Model_Name (m_Info.BaseModelName);
pmodel->Set_Sub_Objects_Match_LOD ((m_MiscInfo.Flags & W3D_AGGREGATE_FORCE_SUB_OBJ_LOD) == W3D_AGGREGATE_FORCE_SUB_OBJ_LOD);
} else {
WWDEBUG_SAY (("Unable to load aggregate %s.\r\n", m_Info.BaseModelName));
}
// Return a pointer to the new aggregate
return pmodel;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Find_Subobject
//
RenderObjClass *
AggregateDefClass::Find_Subobject
(
RenderObjClass &model,
const char mesh_path[MESH_PATH_ENTRIES][MESH_PATH_ENTRY_LEN],
const char bone_path[MESH_PATH_ENTRIES][MESH_PATH_ENTRY_LEN]
)
{
RenderObjClass *parent_model = &model;
parent_model->Add_Ref ();
// Loop through all the models in our "path" until we've either failed
// or found the exact mesh we were looking for...
for (int index = 1;
(mesh_path[index][0] != 0) && (parent_model != NULL);
index ++) {
// Look one level deeper into the subobject chain...
RenderObjClass *sub_obj = NULL;
if (bone_path[index][0] == 0) {
sub_obj = parent_model->Get_Sub_Object_By_Name (mesh_path[index]);
} else {
int bone_index = parent_model->Get_Bone_Index (bone_path[index]);
int subobj_count = parent_model->Get_Num_Sub_Objects_On_Bone (bone_index);
// Loop through all the subobjects on this bone
for (int subobj_index = 0; (subobj_index < subobj_count) && (sub_obj == NULL); subobj_index ++) {
// Is this the subobject we were looking for?
RenderObjClass *ptemp_obj = parent_model->Get_Sub_Object_On_Bone (subobj_index, bone_index);
if (::lstrcmpi (ptemp_obj->Get_Name (), mesh_path[index]) == 0) {
sub_obj = ptemp_obj;
} else {
REF_PTR_RELEASE (ptemp_obj);
}
}
}
REF_PTR_RELEASE (parent_model);
// The parent for the next iteration is the subobject on this one.
parent_model = sub_obj;
}
// Return a pointer to the subobject
return parent_model;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Attach_Subobjects
//
void
AggregateDefClass::Attach_Subobjects (RenderObjClass &base_model)
{
// Now loop through all the subobjects and attach them to the appropriate bone
for (int index = 0; index < m_SubobjectList.Count (); index ++) {
W3dAggregateSubobjectStruct *psubobj_info = m_SubobjectList[index];
if (psubobj_info != NULL) {
// Now create this subobject and attach it to its bone.
RenderObjClass *prender_obj = Create_Render_Object (psubobj_info->SubobjectName);
if (prender_obj != NULL) {
// Attach this object to the requested bone
if (base_model.Add_Sub_Object_To_Bone (prender_obj, psubobj_info->BoneName) == false) {
WWDEBUG_SAY (("Unable to attach %s to %s.\r\n", psubobj_info->SubobjectName, psubobj_info->BoneName));
}
// Release our hold on this pointer
prender_obj->Release_Ref ();
} else {
WWDEBUG_SAY (("Unable to load aggregate subobject %s.\r\n", psubobj_info->SubobjectName));
}
}
}
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Create_Render_Object
//
RenderObjClass *
AggregateDefClass::Create_Render_Object (const char *passet_name)
{
// Assume failure
RenderObjClass *prender_obj = NULL;
// Attempt to get an instance of the render object from the asset manager
prender_obj = WW3DAssetManager::Get_Instance()->Create_Render_Obj (passet_name);
// If we couldn't find the render object in the asset manager, then attempt to
// load it from file
if ((prender_obj == NULL) &&
Load_Assets (passet_name)) {
// It should be in the asset manager now, so attempt to get it again.
prender_obj = WW3DAssetManager::Get_Instance()->Create_Render_Obj (passet_name);
}
// Return a pointer to the render object
return prender_obj;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Load_Assets
//
bool
AggregateDefClass::Load_Assets (const char *passet_name)
{
// Assume failure
bool retval = false;
// Param OK?
if (passet_name != NULL) {
// Determine what the current working directory is
char path[MAX_PATH];
::GetCurrentDirectory (sizeof (path), path);
// Ensure the path is directory delimited
if (path[::lstrlen(path)-1] != '\\') {
::lstrcat (path, "\\");
}
// Assume the filename is simply the "asset name" + the w3d extension
::lstrcat (path, passet_name);
::lstrcat (path, ".w3d");
// If the file exists, then load it into the asset manager.
if (::GetFileAttributes (path) != 0xFFFFFFFF) {
retval = WW3DAssetManager::Get_Instance()->Load_3D_Assets (path);
}
}
// Return the true/false result code
return retval;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Initialize
//
void
AggregateDefClass::Initialize (RenderObjClass &base_model)
{
// Start with fresh lists
Free_Subobject_List ();
// Determine what the render objects original name was.
const char *orig_model_name = base_model.Get_Base_Model_Name ();
orig_model_name = (orig_model_name == NULL) ? base_model.Get_Name () : orig_model_name;
// Record information about this base model
::lstrcpy (m_Info.BaseModelName, orig_model_name);
m_Info.SubobjectCount = 0;
m_MiscInfo.OriginalClassID = base_model.Class_ID ();
m_MiscInfo.Flags = 0;
m_MiscInfo.Flags |= base_model.Is_Sub_Objects_Match_LOD_Enabled () ? W3D_AGGREGATE_FORCE_SUB_OBJ_LOD : 0;
// Pass the aggregate name along
Set_Name (base_model.Get_Name ());
// Create a new instance of the model which we can use
// to compare with the supplied model and determine
// which 'bones-models' and textures are new.
RenderObjClass *pvanilla_model = (RenderObjClass *)Create_Render_Object (orig_model_name);
// Build lists of changes from the delta between the original model and the provided one
Build_Subobject_List (*pvanilla_model, base_model);
// Release the model if necessary
REF_PTR_RELEASE (pvanilla_model);
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Build_Subobject_List
//
void
AggregateDefClass::Build_Subobject_List
(
RenderObjClass &original_model,
RenderObjClass &model
)
{
int index;
// Loop through all the bones in this render obj
int bone_count = model.Get_Num_Bones ();
for (int bone_index = 0; bone_index < bone_count; bone_index ++) {
const char *pbone_name = model.Get_Bone_Name (bone_index);
// Build a list of nodes that are contained in the vanilla model
DynamicVectorClass <RenderObjClass *> orig_node_list;
for (index = 0;
index < original_model.Get_Num_Sub_Objects_On_Bone (bone_index);
index ++) {
RenderObjClass *psubobj = original_model.Get_Sub_Object_On_Bone (index, bone_index);
if (psubobj != NULL) {
orig_node_list.Add (psubobj);
}
}
// Build a list of nodes that are contained in this bone
DynamicVectorClass <RenderObjClass *> node_list;
for (index = 0;
index < model.Get_Num_Sub_Objects_On_Bone (bone_index);
index ++) {
RenderObjClass *psubobj = model.Get_Sub_Object_On_Bone (index, bone_index);
if (psubobj != NULL) {
node_list.Add (psubobj);
}
}
int node_count = node_list.Count ();
if (node_count > 0) {
// Loop through the subobjects and add each one to our internal list
W3dAggregateSubobjectStruct subobj_info = { 0 };
for (int node_index = 0; node_index < node_count; node_index ++) {
RenderObjClass *psubobject = node_list[node_index];
WWASSERT (psubobject != NULL);
// Is this subobject new? (i.e. not in a 'vanilla' instance?)
const char *prototype_name = psubobject->Get_Name ();
if (psubobject != NULL &&
(Is_Object_In_List (prototype_name, orig_node_list) == false)) {
// Add this subobject to our list
::lstrcpy (subobj_info.SubobjectName, prototype_name);
::lstrcpy (subobj_info.BoneName, pbone_name);
Add_Subobject (subobj_info);
m_Info.SubobjectCount ++;
// Attach this render object to the 'original' model (this is done
// so we can do texture compares later)
RenderObjClass *prender_obj = WW3DAssetManager::Get_Instance ()->Create_Render_Obj (prototype_name);
((RenderObjClass &)original_model).Add_Sub_Object_To_Bone (prender_obj, pbone_name);
REF_PTR_RELEASE (prender_obj);
}
}
}
// Free our hold on the render objs in the original node list
for (index = 0; index < orig_node_list.Count (); index ++) {
REF_PTR_RELEASE (orig_node_list[index]);
}
orig_node_list.Delete_All ();
// Free our hold on the render objs in the node list
for (index = 0; index < node_list.Count (); index ++) {
REF_PTR_RELEASE (node_list[index]);
}
node_list.Delete_All ();
}
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Is_Object_In_List
//
bool
AggregateDefClass::Is_Object_In_List
(
const char *passet_name,
DynamicVectorClass <RenderObjClass *> &node_list
)
{
// Assume failure
bool retval = false;
// Loop through the nodes in the list until we've found the one
// were are looking for.
for (int node_index = 0; (node_index < node_list.Count ()) && (retval == false); node_index ++) {
RenderObjClass *prender_obj = node_list[node_index];
// Is this the render object we were looking for?
if (prender_obj != NULL &&
::lstrcmpi (prender_obj->Get_Name (), passet_name) == 0) {
retval = true;
}
}
// Return the true/false result code
return retval;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Load
//
WW3DErrorType
AggregateDefClass::Load_W3D (ChunkLoadClass &chunk_load)
{
W3dTextureReplacerHeaderStruct header = { 0 };
while (chunk_load.Open_Chunk()) {
WW3DErrorType error = WW3D_ERROR_OK;
switch (chunk_load.Cur_Chunk_ID()) {
case W3D_CHUNK_AGGREGATE_HEADER:
error = Read_Header(chunk_load);
break;
case W3D_CHUNK_AGGREGATE_INFO:
error = Read_Info(chunk_load);
break;
case W3D_CHUNK_TEXTURE_REPLACER_INFO:
if (chunk_load.Read (&header, sizeof (header)) == sizeof (header)) {
if (header.ReplacedTexturesCount > 0) {
WWDEBUG_SAY(("Obsolete texture replacement chunk encountered in aggregate: %s\r\n",m_pName));
}
}
break;
case W3D_CHUNK_AGGREGATE_CLASS_INFO:
error = Read_Class_Info(chunk_load);
break;
default:
// Unknown chunk.
break;
}
chunk_load.Close_Chunk();
if (error != WW3D_ERROR_OK) return (error);
}
return WW3D_ERROR_OK;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Read_Header
//
WW3DErrorType
AggregateDefClass::Read_Header (ChunkLoadClass &chunk_load)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_LOAD_FAILED;
// Is this the header chunk?
W3dAggregateHeaderStruct header = { 0 };
if (chunk_load.Read (&header, sizeof (header)) == sizeof (header)) {
// Copy the name from the header structure
m_pName = ::_strdup (header.Name);
m_Version = header.Version;
// Success!
ret_val = WW3D_ERROR_OK;
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Read_Info
//
WW3DErrorType
AggregateDefClass::Read_Info (ChunkLoadClass &chunk_load)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_LOAD_FAILED;
// Read the chunk straight into our member structure
::memset (&m_Info, 0, sizeof (m_Info));
if (chunk_load.Read (&m_Info, sizeof (m_Info)) == sizeof (m_Info)) {
// Success!
ret_val = WW3D_ERROR_OK;
// Read all the subobjects from the file
for (UINT isubobject = 0;
(isubobject < m_Info.SubobjectCount) && (ret_val == WW3D_ERROR_OK);
isubobject ++) {
// Read this subobject's definition from the file
ret_val = Read_Subobject (chunk_load);
}
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Read_Subobject
//
WW3DErrorType
AggregateDefClass::Read_Subobject (ChunkLoadClass &chunk_load)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_LOAD_FAILED;
// Read the subobject information from the file
W3dAggregateSubobjectStruct subobj_info = { 0 };
if (chunk_load.Read (&subobj_info, sizeof (subobj_info)) == sizeof (subobj_info)) {
// Add this subobject to our list
Add_Subobject (subobj_info);
// Success!
ret_val = WW3D_ERROR_OK;
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Add_Subobject
//
void
AggregateDefClass::Add_Subobject (const W3dAggregateSubobjectStruct &subobj_info)
{
// Create a new structure and copy the contents of the src
W3dAggregateSubobjectStruct *pnew_entry = W3DNEW W3dAggregateSubobjectStruct;
::lstrcpy (pnew_entry->SubobjectName, subobj_info.SubobjectName);
::lstrcpy (pnew_entry->BoneName, subobj_info.BoneName);
// Add this new entry to the list
m_SubobjectList.Add (pnew_entry);
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Read_Class_Info
//
WW3DErrorType
AggregateDefClass::Read_Class_Info (ChunkLoadClass &chunk_load)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_LOAD_FAILED;
// Read the chunk straight into our header structure
::memset (&m_MiscInfo, 0, sizeof (m_MiscInfo));
if (chunk_load.Read (&m_MiscInfo, sizeof (m_MiscInfo)) == sizeof (m_MiscInfo)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
//////////////////////////////////////////////////////////////////////////////////
//
// Save
//
WW3DErrorType
AggregateDefClass::Save_W3D (ChunkSaveClass &chunk_save)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Begin a chunk that identifies an aggregate
if (chunk_save.Begin_Chunk (W3D_CHUNK_AGGREGATE) == TRUE) {
// Attempt to save the different sections of the aggregate definition
if ((Save_Header (chunk_save) == WW3D_ERROR_OK) &&
(Save_Info (chunk_save) == WW3D_ERROR_OK) &&
(Save_Class_Info (chunk_save) == WW3D_ERROR_OK)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// Close the aggregate chunk
chunk_save.End_Chunk ();
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Save_Header
//
WW3DErrorType
AggregateDefClass::Save_Header (ChunkSaveClass &chunk_save)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Begin a chunk that identifies the aggregate
if (chunk_save.Begin_Chunk (W3D_CHUNK_AGGREGATE_HEADER) == TRUE) {
// Fill the header structure
W3dAggregateHeaderStruct header = { 0 };
header.Version = W3D_CURRENT_AGGREGATE_VERSION;
::lstrcpyn (header.Name, m_pName, sizeof (header.Name));
header.Name[sizeof (header.Name) - 1] = 0;
// Write the header out to the chunk
if (chunk_save.Write (&header, sizeof (header)) == sizeof (header)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// End the header chunk
chunk_save.End_Chunk ();
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Save_Info
//
WW3DErrorType
AggregateDefClass::Save_Info (ChunkSaveClass &chunk_save)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Begin a chunk that identifies the aggregate settings
if (chunk_save.Begin_Chunk (W3D_CHUNK_AGGREGATE_INFO) == TRUE) {
// Write the settings structure out to the chunk
if (chunk_save.Write (&m_Info, sizeof (m_Info)) == sizeof (m_Info)) {
// Success!
ret_val = WW3D_ERROR_OK;
// Write all the subobjects to the file
for (int isubobject = 0;
(isubobject < m_SubobjectList.Count ()) && (ret_val == WW3D_ERROR_OK);
isubobject ++) {
// Write this object to the file
ret_val = Save_Subobject (chunk_save, m_SubobjectList[isubobject]);
}
}
// End the settings chunk
chunk_save.End_Chunk ();
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Save_Subobject
//
WW3DErrorType
AggregateDefClass::Save_Subobject
(
ChunkSaveClass &chunk_save,
W3dAggregateSubobjectStruct *psubobject
)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Write the subobj structure out to the chunk
if (chunk_save.Write (psubobject, sizeof (W3dAggregateSubobjectStruct)) == sizeof (W3dAggregateSubobjectStruct)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
/////////////////////////////////////////////////////////////////////////////////
//
// Save_Class_Info
//
WW3DErrorType
AggregateDefClass::Save_Class_Info (ChunkSaveClass &chunk_save)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Begin a chunk that identifies the texture replacer header
if (chunk_save.Begin_Chunk (W3D_CHUNK_AGGREGATE_CLASS_INFO) == TRUE) {
// Write the class information structure out to the chunk
if (chunk_save.Write (&m_MiscInfo, sizeof (m_MiscInfo)) == sizeof (m_MiscInfo)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// End the class info chunk
chunk_save.End_Chunk ();
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Load
//
PrototypeClass *
AggregateLoaderClass::Load_W3D (ChunkLoadClass &chunk_load)
{
// Assume failure
AggregatePrototypeClass *pprototype = NULL;
// Create a definition object
AggregateDefClass *pdefinition = W3DNEW AggregateDefClass;
if (pdefinition != NULL) {
// Ask the definition object to load the aggregate data
if (pdefinition->Load_W3D (chunk_load) != WW3D_ERROR_OK) {
// Error! Free the definition
delete pdefinition;
pdefinition = NULL;
} else {
// Success! Create a prototype from the definition
pprototype = W3DNEW AggregatePrototypeClass (pdefinition);
}
}
// Return a pointer to the prototype
return pprototype;
}

View file

@ -0,0 +1,259 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/agg_def.h $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 4/05/01 9:52a $*
* *
* $Revision:: 2 $*
* *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef AGGREGATE_DEF_H
#define AGGREGATE_DEF_H
#include "proto.h"
#include "rendobj.h"
#include "w3d_file.h"
#include "w3derr.h"
#include "vector.h"
#include "bittype.h"
#include <string.h>
#ifdef _UNIX
#include "osdep.h"
#endif
// Forward declarations
class ChunkLoadClass;
class ChunkSaveClass;
class IndirectTextureClass;
///////////////////////////////////////////////////////////////////////////////////
//
// Macros
//
#ifndef SAFE_FREE
#define SAFE_FREE(pointer) \
{ \
if (pointer) { \
::free (pointer); \
pointer = 0; \
} \
}
#endif //SAFE_FREE
//////////////////////////////////////////////////////////////////////////////////
//
// AggregateDefClass
//
// Description of an aggregate object. Used by the asset manager
// to construct aggregates. An 'aggregate' is simply a 'shell' that
// contains references to a hierarchy model and subobjects to attach to its bones.
//
class AggregateDefClass
{
public:
///////////////////////////////////////////////////////////
//
// Public constructors/destructors
//
AggregateDefClass (void);
AggregateDefClass (RenderObjClass &base_model);
AggregateDefClass (const AggregateDefClass &src);
virtual ~AggregateDefClass (void);
///////////////////////////////////////////////////////////
//
// Public operators
//
const AggregateDefClass &operator= (const AggregateDefClass &src);
///////////////////////////////////////////////////////////
//
// Public methods
//
virtual WW3DErrorType Load_W3D (ChunkLoadClass &chunk_load);
virtual WW3DErrorType Save_W3D (ChunkSaveClass &chunk_save);
const char * Get_Name (void) const { return m_pName; }
void Set_Name (const char *pname) { SAFE_FREE (m_pName); m_pName = ::_strdup (pname); }
RenderObjClass * Create (void);
AggregateDefClass * Clone (void) const { return W3DNEW AggregateDefClass (*this); }
//
// Public accessors
//
ULONG Class_ID (void) const { return m_MiscInfo.OriginalClassID; }
//
// Initialization
//
void Initialize (RenderObjClass &base_model);
protected:
///////////////////////////////////////////////////////////
//
// Protected data types
//
typedef struct _TEXTURE_INFO
{
W3dTextureReplacerStruct names;
IndirectTextureClass * pnew_texture;
bool operator == (_TEXTURE_INFO &src) { return false; }
bool operator != (_TEXTURE_INFO &src) { return true; }
} TEXTURE_INFO;
///////////////////////////////////////////////////////////
//
// Protected methods
//
//
// Loading methods
//
virtual WW3DErrorType Read_Header (ChunkLoadClass &chunk_load);
virtual WW3DErrorType Read_Info (ChunkLoadClass &chunk_load);
virtual WW3DErrorType Read_Subobject (ChunkLoadClass &chunk_load);
virtual WW3DErrorType Read_Class_Info (ChunkLoadClass &chunk_load);
//
// Saving methods
//
virtual WW3DErrorType Save_Header (ChunkSaveClass &chunk_save);
virtual WW3DErrorType Save_Info (ChunkSaveClass &chunk_save);
virtual WW3DErrorType Save_Subobject (ChunkSaveClass &chunk_save, W3dAggregateSubobjectStruct *psubobject);
virtual WW3DErrorType Save_Class_Info (ChunkSaveClass &chunk_save);
//
// Creation methods
//
virtual void Attach_Subobjects (RenderObjClass &base_model);
//
// Search methods
//
virtual RenderObjClass * Find_Subobject (RenderObjClass &model, const char mesh_path[MESH_PATH_ENTRIES][MESH_PATH_ENTRY_LEN], const char bone_path[MESH_PATH_ENTRIES][MESH_PATH_ENTRY_LEN]);
//
// Misc. methods
//
virtual void Free_Subobject_List (void);
virtual void Add_Subobject (const W3dAggregateSubobjectStruct &subobj_info);
virtual bool Load_Assets (const char *asset_name);
virtual RenderObjClass *Create_Render_Object (const char *passet_name);
virtual bool Is_Object_In_List (const char *passet_name, DynamicVectorClass <RenderObjClass *> &node_list);
virtual void Build_Subobject_List (RenderObjClass &original_model, RenderObjClass &model);
private:
///////////////////////////////////////////////////////////
//
// Private member data
//
DWORD m_Version;
DynamicVectorClass<W3dAggregateSubobjectStruct *> m_SubobjectList;
W3dAggregateInfoStruct m_Info;
W3dAggregateMiscInfo m_MiscInfo;
char * m_pName;
};
///////////////////////////////////////////////////////////////////////////////////
//
// AggregatePrototypeClass
//
class AggregatePrototypeClass : public W3DMPO, public PrototypeClass
{
W3DMPO_GLUE(AggregatePrototypeClass)
public:
///////////////////////////////////////////////////////////
//
// Public constructors/destructors
//
AggregatePrototypeClass (AggregateDefClass *pdef) { m_pDefinition = pdef; }
///////////////////////////////////////////////////////////
//
// Public methods
//
virtual const char * Get_Name(void) const { return m_pDefinition->Get_Name (); }
virtual int Get_Class_ID(void) const { return m_pDefinition->Class_ID (); }
virtual RenderObjClass * Create (void) { return m_pDefinition->Create (); }
virtual void DeleteSelf() { delete this; }
virtual AggregateDefClass * Get_Definition (void) const { return m_pDefinition; }
virtual void Set_Definition (AggregateDefClass *pdef) { m_pDefinition = pdef; }
protected:
virtual ~AggregatePrototypeClass (void) { delete m_pDefinition; }
private:
///////////////////////////////////////////////////////////
//
// Private member data
//
AggregateDefClass * m_pDefinition;
};
///////////////////////////////////////////////////////////////////////////////////
//
// AggregateLoaderClass
//
class AggregateLoaderClass : public PrototypeLoaderClass
{
public:
virtual int Chunk_Type (void) { return W3D_CHUNK_AGGREGATE; }
virtual PrototypeClass * Load_W3D (ChunkLoadClass &chunk_load);
};
///////////////////////////////////////////////////////////////////////////////////
//
// Global variables
//
extern AggregateLoaderClass _AggregateLoader;
#endif //__AGGREGATE_DEF_H

View file

@ -0,0 +1,565 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d2 *
* *
* $Archive:: /Commando/Code/ww3d2/animatedsoundmgr.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 12/13/01 6:05p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//
// MBL Update for CNC3 INCURSION - 10.23.2002 - Expanded param handling, Added STOP command
//
#include <string.h> // stricmp()
#include "animatedsoundmgr.h"
#include "ini.h"
#include "inisup.h"
#include "ffactory.h"
#include "wwfile.h"
#include <stdio.h>
#include "definition.h"
#include "definitionmgr.h"
#include "definitionclassids.h"
#include "wwaudio.h"
#include "audiblesound.h"
#include "htree.h"
#include "hanim.h"
#include "soundlibrarybridge.h"
#include "WWDebug.h"
//////////////////////////////////////////////////////////////////////
// Static member initialization
//////////////////////////////////////////////////////////////////////
HashTemplateClass<StringClass, AnimatedSoundMgrClass::ANIM_SOUND_LIST *> AnimatedSoundMgrClass::AnimationNameHash;
DynamicVectorClass<AnimatedSoundMgrClass::ANIM_SOUND_LIST *> AnimatedSoundMgrClass::AnimSoundLists;
SoundLibraryBridgeClass* AnimatedSoundMgrClass::SoundLibrary = NULL;
//////////////////////////////////////////////////////////////////////
// Local inlines
//////////////////////////////////////////////////////////////////////
static WWINLINE INIClass *
Get_INI (const char *filename)
{
INIClass *ini = NULL;
//
// Get the file from our filefactory
//
FileClass *file = _TheFileFactory->Get_File (filename);
if (file) {
//
// Create the INI object
//
if (file->Is_Available ()) {
ini = new INIClass (*file);
}
//
// Close the file
//
_TheFileFactory->Return_File (file);
}
return ini;
}
static int
Build_List_From_String
(
const char * buffer,
const char * delimiter,
StringClass ** string_list
)
{
int count = 0;
WWASSERT (buffer != NULL);
WWASSERT (delimiter != NULL);
WWASSERT (string_list != NULL);
if ((buffer != NULL) &&
(delimiter != NULL) &&
(string_list != NULL))
{
int delim_len = ::strlen (delimiter);
//
// Determine how many entries there will be in the list
//
for (const char *entry = buffer;
(entry != NULL) && (entry[1] != 0);
entry = ::strstr (entry, delimiter))
{
//
// Move past the current delimiter (if necessary)
//
if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
entry += delim_len;
}
// Increment the count of entries
count ++;
}
if (count > 0) {
//
// Allocate enough StringClass objects to hold all the strings in the list
//
(*string_list) = new StringClass[count];
//
// Parse the string and pull out its entries.
//
count = 0;
for (entry = buffer;
(entry != NULL) && (entry[1] != 0);
entry = ::strstr (entry, delimiter))
{
//
// Move past the current delimiter (if necessary)
//
if ((::strnicmp (entry, delimiter, delim_len) == 0) && (count > 0)) {
entry += delim_len;
}
//
// Copy this entry into its own string
//
StringClass entry_string = entry;
char *delim_start = ::strstr (entry_string, delimiter);
if (delim_start != NULL) {
delim_start[0] = 0;
}
//
// Add this entry to our list
//
if ((entry_string.Get_Length () > 0) || (count == 0)) {
(*string_list)[count++] = entry_string;
}
}
} else if (delim_len > 0) {
count = 1;
(*string_list) = new StringClass[count];
(*string_list)[0] = buffer;
}
}
//
// Return the number of entries in our list
//
return count;
}
static bool
Is_In_Param_List
(
StringClass *param_list,
int param_count,
const char *param_to_check
)
{
//
// Check incoming parameters
//
WWASSERT( param_list != NULL );
if ( param_list == NULL )
{
return( false );
}
WWASSERT( param_count >= 2 );
if ( param_count < 2 )
{
return( false );
}
WWASSERT( param_to_check != NULL );
if ( param_to_check == NULL )
{
return( false );
}
//
// Note: params 0 & 1 are fixed to frame and name...
//
for ( int param_index = 2; param_index < param_count; param_index ++ )
{
{
StringClass string = param_list[ param_index ];
// OutputDebugString( "MBL: Comparing " );
// OutputDebugString( string.Peek_Buffer() );
// OutputDebugString( " with " );
// OutputDebugString( param_to_check );
// OutputDebugString( "\n" );
// if ( stricmp( string.Peek_Buffer(), param_to_check ) == 0 ) // Breaks with whitespaces
if ( strstr( string.Peek_Buffer(), param_to_check ) != 0 )
{
return( true );
}
}
}
return( false );
}
//////////////////////////////////////////////////////////////////////
//
// Initialize
//
//////////////////////////////////////////////////////////////////////
void
AnimatedSoundMgrClass::Initialize (const char *ini_filename)
{
//
// Don't re-initialize...
//
if (AnimSoundLists.Count () > 0) {
return ;
}
const char *DEFAULT_INI_FILENAME = "w3danimsound.ini";
//
// Determine which filename to use
//
const char *filename_to_use = ini_filename;
if (filename_to_use == NULL) {
filename_to_use = DEFAULT_INI_FILENAME;
}
//
// Get the INI file which contains the data for this viewer
//
INIClass *ini_file = ::Get_INI (filename_to_use);
if (ini_file != NULL) {
//
// Loop over all the sections in the INI
//
List<INISection *> &section_list = ini_file->Get_Section_List ();
for ( INISection *section = section_list.First ();
section != NULL && section->Is_Valid ();
section = section->Next_Valid ())
{
//
// Get the animation name from the section name
//
StringClass animation_name = section->Section;
::strupr (animation_name.Peek_Buffer ());
// OutputDebugString( "MBL Section / animation: " );
// OutputDebugString( animation_name.Peek_Buffer() );
// OutputDebugString( "\n" );
//
// Allocate a sound list
//
ANIM_SOUND_LIST *sound_list = new ANIM_SOUND_LIST;
//
// Loop over all the entries in this section
//
int entry_count = ini_file->Entry_Count (section->Section);
for (int entry_index = 0; entry_index < entry_count; entry_index ++) {
StringClass value;
//
// Get the data associated with this entry
//
const char *entry_name = ini_file->Get_Entry (section->Section, entry_index);
// OutputDebugString( " MBL Entry name: " );
// OutputDebugString( entry_name );
// OutputDebugString( "\n" );
if (strcmp(entry_name, "BoneName") == 0) {
ini_file->Get_String (value, section->Section, entry_name);
sound_list->BoneName = value;
// OutputDebugString( " MBL (BoneName) entry line value: " );
// OutputDebugString( value.Peek_Buffer() );
// OutputDebugString( "\n" );
} else {
ini_file->Get_String (value, section->Section, entry_name);
// OutputDebugString( " MBL (not BoneName) entry line value: " );
// OutputDebugString( value.Peek_Buffer() );
// OutputDebugString( "\n" );
//
// Extract the parameters from the section
//
int len = value.Get_Length ();
StringClass definition_name (len + 1, true);
int action_frame = 0;
//
// Separate the parameters into an easy-to-handle data structure
//
StringClass *param_list = NULL;
int param_count = ::Build_List_From_String (value, ",", &param_list);
// if ((param_count >= 2) && (param_count <= 3))
{
action_frame = ::atoi (param_list[0]);
definition_name = param_list[1];
definition_name.Trim ();
//
// Tie the relevant information together and store it
// in the list of sounds for this animation
//
ANIM_SOUND_INFO* sound_info = new ANIM_SOUND_INFO;
sound_info->Frame = action_frame;
sound_info->SoundName = definition_name;
//
// "2D" check
//
// if ((param_count == 3) && (atoi(param_list[2]) == 2)) {
// sound_info->Is2D = true;
// }
//
sound_info->Is2D = false;
if ( Is_In_Param_List( param_list, param_count, "2D" ) )
{
sound_info->Is2D = true;
}
//
// "STOP" check
//
sound_info->IsStop = false;
if ( Is_In_Param_List( param_list, param_count, "STOP" ) )
{
sound_info->IsStop = true;
}
sound_list->Add_Sound_Info (sound_info);
delete [] param_list;
}
}
}
if (sound_list->List.Count () != 0) {
//
// Add this sound list to our hash-table and vector-array
//
AnimationNameHash.Insert (animation_name, sound_list);
AnimSoundLists.Add (sound_list);
} else {
//WWDEBUG_SAY (("AnimatedSoundMgrClass::Initialize -- No sounds added for %d!\n", animation_name.Peek_Buffer ()));
delete sound_list;
}
}
delete ini_file;
}
return ;
}
//////////////////////////////////////////////////////////////////////
//
// Shutdown
//
//////////////////////////////////////////////////////////////////////
void
AnimatedSoundMgrClass::Shutdown (void)
{
//
// Reset the animation name hash
//
AnimationNameHash.Remove_All ();
//
// Free each of the sound objects
//
for (int index = 0; index < AnimSoundLists.Count (); index ++) {
/*
ANIM_SOUND_LIST* list = AnimSoundLists[index];
for (int i = 0; i < list->Count(); i++) {
delete (*list)[i];
}
*/
delete AnimSoundLists[index];
}
AnimSoundLists.Delete_All ();
return ;
}
//////////////////////////////////////////////////////////////////////
//
// Does_Animation_Have_Embedded_Sounds
//
//////////////////////////////////////////////////////////////////////
const char*
AnimatedSoundMgrClass::Get_Embedded_Sound_Name (HAnimClass *anim)
{
if (anim == NULL) {
return NULL;
}
ANIM_SOUND_LIST* list = Find_Sound_List (anim);
if (list == NULL) {
return NULL;
}
return list->BoneName.Peek_Buffer();
}
//////////////////////////////////////////////////////////////////////
//
// Find_Sound_List
//
//////////////////////////////////////////////////////////////////////
AnimatedSoundMgrClass::ANIM_SOUND_LIST *
AnimatedSoundMgrClass::Find_Sound_List (HAnimClass *anim)
{
//
// Build the full name of the animation
//
StringClass full_name (0, true);
full_name = anim->Get_Name ();
//
// Make the name uppercase
//
::strupr (full_name.Peek_Buffer ());
//
// Lookup the sound list for this animation
//
ANIM_SOUND_LIST *retval = AnimationNameHash.Get (full_name);
return retval;
}
//////////////////////////////////////////////////////////////////////
//
// Trigger_Sound
//
//////////////////////////////////////////////////////////////////////
float
AnimatedSoundMgrClass::Trigger_Sound
(
HAnimClass * anim,
float old_frame,
float new_frame,
const Matrix3D & tm
)
{
if ((SoundLibrary == NULL) || (anim == NULL)) {
return old_frame;
}
float retval = old_frame;
#ifndef W3D_MAX4
//
// Lookup the sound list for this animation
//
ANIM_SOUND_LIST *sound_list = Find_Sound_List (anim);
if (sound_list != NULL) {
for (int index = 0; index < sound_list->List.Count (); index ++) {
int frame = sound_list->List[index]->Frame;
//
// Is the animation passing the frame we need?
//
if ((old_frame < frame) && (new_frame >= frame)) {
//
// Don't trigger the sound if its skipped too far past...
//
//if (WWMath::Fabs (new_frame - old_frame) < 3.0F) {
//
// Stop the audio?
//
if (sound_list->List[index]->IsStop == true)
{
//
// Stop the audio
//
SoundLibrary->Stop_Playing_Audio( sound_list->List[index]->SoundName.Peek_Buffer() );
}
else
{
//
// Play the audio
//
if (sound_list->List[index]->Is2D == true)
{
SoundLibrary->Play_2D_Audio(sound_list->List[index]->SoundName.Peek_Buffer());
}
else
{
SoundLibrary->Play_3D_Audio(sound_list->List[index]->SoundName.Peek_Buffer(), tm);
}
}
//WWDEBUG_SAY (("Triggering Sound %d %s\n", GetTickCount (), sound_list->List[index]->SoundName));
retval = frame;
//}
}
}
//retval = true;
}
#endif
return retval;
}
void AnimatedSoundMgrClass::Set_Sound_Library(SoundLibraryBridgeClass* library)
{
SoundLibrary = library;
}

View file

@ -0,0 +1,136 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d2 *
* *
* $Archive:: /Commando/Code/ww3d2/animatedsoundmgr.h $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 12/13/01 6:05p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//
// MBL Update for CNC3 INCURSION - 10.23.2002 - Expanded param handling, Added STOP command
//
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef __ANIMATEDSOUNDMGR_H
#define __ANIMATEDSOUNDMGR_H
#include "simplevec.h"
#include "vector.h"
#include "hashtemplate.h"
//////////////////////////////////////////////////////////////////////
// Forward declarations
//////////////////////////////////////////////////////////////////////
class HTreeClass;
class HAnimClass;
class Matrix3D;
class SoundLibraryBridgeClass;
//////////////////////////////////////////////////////////////////////
//
// AnimatedSoundMgrClass
//
//////////////////////////////////////////////////////////////////////
class AnimatedSoundMgrClass
{
public:
///////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////
//
// Initialization and shutdown
//
static void Initialize (const char *ini_filename = NULL);
static void Shutdown (void);
//
// Sound playback
//
static const char* Get_Embedded_Sound_Name (HAnimClass *anim);
static float Trigger_Sound (HAnimClass *anim, float old_frame, float new_frame, const Matrix3D &tm);
// Bridges E&B code with WW3D.
static void Set_Sound_Library(SoundLibraryBridgeClass* library);
private:
///////////////////////////////////////////////////////////////////
// Private data types
///////////////////////////////////////////////////////////////////
struct AnimSoundInfo
{
AnimSoundInfo() : Frame(0), SoundName(), Is2D(false), IsStop(false) {}
int Frame;
StringClass SoundName;
bool Is2D;
bool IsStop;
};
typedef AnimSoundInfo ANIM_SOUND_INFO;
struct AnimSoundList
{
AnimSoundList() : List(), BoneName("root") {}
~AnimSoundList()
{
for (int i = 0; i < List.Count(); i++) {
delete List[i];
}
}
void Add_Sound_Info(ANIM_SOUND_INFO* info) {List.Add(info);}
SimpleDynVecClass<ANIM_SOUND_INFO*> List;
StringClass BoneName;
};
typedef AnimSoundList ANIM_SOUND_LIST;
///////////////////////////////////////////////////////////////////
// Private member data
///////////////////////////////////////////////////////////////////
static HashTemplateClass<StringClass, ANIM_SOUND_LIST *> AnimationNameHash;
static DynamicVectorClass<ANIM_SOUND_LIST *> AnimSoundLists;
static SoundLibraryBridgeClass* SoundLibrary;
///////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////
static ANIM_SOUND_LIST * Find_Sound_List (HAnimClass *anim);
};
#endif //__ANIMATEDSOUNDMGR_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,328 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/animobj.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 12/10/01 11:18a $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* Animatable3DObjClass::Base_Update -- animation update function for the base pose *
* Animatable3DObjClass::Anim_Update -- Update function for a single animation *
* Animatable3DObjClass::Blend_Update -- update function for a blend of two animations *
* Animatable3DObjClass::Combo_Update -- Animation update for a combination of anims *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef ANIMOBJ_H
#define ANIMOBJ_H
#include "always.h"
#include "composite.h"
#include "htree.h"
#include "hanim.h"
class SkinClass;
class RenderInfoClass;
/**
** Animatable3DObjClass
** This class performs some of the work necessary to implement hierarchical animation.
** It implements much of the bone and animation interface of RenderObjClass.
*/
class Animatable3DObjClass : public CompositeRenderObjClass
{
public:
Animatable3DObjClass(const char * htree_name);
Animatable3DObjClass(const Animatable3DObjClass & src);
Animatable3DObjClass & operator = (const Animatable3DObjClass &);
virtual ~Animatable3DObjClass(void);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Rendering
/////////////////////////////////////////////////////////////////////////////
virtual void Render(RenderInfoClass & rinfo);
virtual void Special_Render(SpecialRenderInfoClass & rinfo);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - "Scene Graph"
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Transform(const Matrix3D &m);
virtual void Set_Position(const Vector3 &v);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Hierarchical Animation
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Animation(void);
virtual void Set_Animation( HAnimClass * motion,
float frame, int anim_mode = ANIM_MODE_MANUAL);
virtual void Set_Animation( HAnimClass * motion0,
float frame0,
HAnimClass * motion1,
float frame1,
float percentage);
virtual void Set_Animation( HAnimComboClass * anim_combo);
virtual void Set_Animation_Frame_Rate_Multiplier(float multiplier); // 020607 srj -- added
virtual HAnimClass * Peek_Animation_And_Info(float& frame, int& numFrames, int& mode, float& mult); // 020710 srj -- added
virtual HAnimClass * Peek_Animation( void );
virtual bool Is_Animation_Complete( void ) const;
virtual int Get_Num_Bones(void);
virtual const char * Get_Bone_Name(int bone_index);
virtual int Get_Bone_Index(const char * bonename);
virtual const Matrix3D & Get_Bone_Transform(const char * bonename);
virtual const Matrix3D & Get_Bone_Transform(int boneindex);
virtual void Capture_Bone(int boneindex);
virtual void Release_Bone(int boneindex);
virtual bool Is_Bone_Captured(int boneindex) const;
virtual void Control_Bone(int bindex,const Matrix3D & objtm,bool world_space_translation = false);
virtual const HTreeClass * Get_HTree(void) const { return HTree; }
//
// Simple bone evaluation methods for when the caller doesn't want
// to update the heirarchy, but needs to know the transform of
// a bone at a given frame.
//
virtual bool Simple_Evaluate_Bone(int boneindex, Matrix3D *tm) const;
virtual bool Simple_Evaluate_Bone(int boneindex, float frame, Matrix3D *tm) const;
// (gth) TESTING DYNAMICALLY SWAPPING SKELETONS!
virtual void Set_HTree(HTreeClass * htree);
///Generals change so we can set sub-object transforms directly without having them revert to base pose
///when marked dirty. DON'T USE THIS UNLESS YOU HAVE A GOOD REASON! -MW
void Friend_Set_Hierarchy_Valid(bool onoff) const { IsTreeValid = onoff; }
protected:
// internally used to compute the current frame if the object is in ANIM_MODE_MANUAL
float Compute_Current_Frame(float *newDirection=NULL) const;
// Update the sub-object transforms according to the current anim state and root transform.
virtual void Update_Sub_Object_Transforms(void);
// Update the transforms using the base pose only
void Base_Update(const Matrix3D & root);
// Update the transforms using a single frame of motion data
void Anim_Update( const Matrix3D & root,
HAnimClass * motion,
float frame);
// Update the transforms blending two frames of motion data
void Blend_Update( const Matrix3D & root,
HAnimClass * motion0,
float frame0,
HAnimClass * motion1,
float frame1,
float percentage);
// Update the transforms with an AnimationCombination
void Combo_Update( const Matrix3D & root,
HAnimComboClass *anim);
// flag to kep track of whether the hierarchy tree transforms are currently valid
bool Is_Hierarchy_Valid(void) const { return IsTreeValid; }
void Set_Hierarchy_Valid(bool onoff) const { IsTreeValid = onoff; }
// Progress anims for single anim (loop and once)
void Single_Anim_Progress( void );
// Release any anims
void Release( void );
protected:
// Is the hierarchy tree currently valid
mutable bool IsTreeValid;
// Hierarchy Tree
HTreeClass * HTree;
// Animation state for the next frame. When we add more flexible motion
// compositing, add a new state and its associated data to the union below
enum {
NONE = 0,
BASE_POSE,
SINGLE_ANIM,
DOUBLE_ANIM,
MULTIPLE_ANIM,
};
int CurMotionMode;
union {
// CurMotionMode == SINGLE_ANIM
struct {
HAnimClass * Motion;
float Frame;
float PrevFrame;
int AnimMode;
mutable int LastSyncTime;
float animDirection;
float frameRateMultiplier; // 020607 srj -- added
} ModeAnim;
// CurMotionMode == DOUBLE_ANIM
struct {
HAnimClass * Motion0;
HAnimClass * Motion1;
float Frame0;
float Frame1;
float PrevFrame0;
float PrevFrame1;
float Percentage;
} ModeInterp;
// CurMotionMode == MULTIPLE_ANIM
struct {
HAnimComboClass * AnimCombo;
} ModeCombo;
};
friend class SkinClass;
};
/***********************************************************************************************
* Animatable3DObjClass::Base_Update -- animation update function for the base pose *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/2/99 GTH : Created. *
*=============================================================================================*/
inline void Animatable3DObjClass::Base_Update(const Matrix3D & root)
{
/*
** This method simply puts the meshes in the base pose's configuration
*/
if (HTree) {
HTree->Base_Update(root);
}
Set_Hierarchy_Valid(true);
}
/***********************************************************************************************
* Animatable3DObjClass::Anim_Update -- Update function for a single animation *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/2/99 GTH : Created. *
*=============================================================================================*/
inline void Animatable3DObjClass::Anim_Update(const Matrix3D & root,HAnimClass * motion,float frame)
{
/*
** Apply motion to the base pose
*/
if ((motion) && (HTree)) {
if (ModeAnim.Motion->Class_ID() == HAnimClass::CLASSID_HRAWANIM)
HTree->Anim_Update(Transform,(HRawAnimClass*)ModeAnim.Motion,ModeAnim.Frame);
else
HTree->Anim_Update(root,motion,frame);
}
Set_Hierarchy_Valid(true);
}
/***********************************************************************************************
* Animatable3DObjClass::Blend_Update -- update function for a blend of two animations *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/2/99 GTH : Created. *
*=============================================================================================*/
inline void Animatable3DObjClass::Blend_Update
(
const Matrix3D & root,
HAnimClass * motion0,
float frame0,
HAnimClass * motion1,
float frame1,
float percentage
)
{
/*
** Apply motion to the base pose
*/
if (HTree) {
HTree->Blend_Update(root,motion0,frame0,motion1,frame1,percentage);
}
Set_Hierarchy_Valid(true);
}
/***********************************************************************************************
* Animatable3DObjClass::Combo_Update -- Animation update for a combination of anims *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/2/99 GTH : Created. *
*=============================================================================================*/
inline void Animatable3DObjClass::Combo_Update( const Matrix3D & root, HAnimComboClass *anim )
{
if (HTree) {
HTree->Combo_Update(root, anim);
}
Set_Hierarchy_Valid(true);
}
#endif //ANIMOBJ_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,446 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Header: /Commando/Code/ww3d2/assetmgr.h 19 12/17/01 7:55p Jani_p $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /Commando/Code/ww3d2/assetmgr.h $*
* *
* Author:: Greg_h *
* *
* $Modtime:: 12/15/01 4:14p $*
* *
* $Revision:: 19 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef ASSETMGR_H
#define ASSETMGR_H
#include "always.h"
#include "vector.h"
#include "htreemgr.h"
#include "hanimmgr.h"
#include "slist.h"
#include "texture.h"
#include "hashtemplate.h"
#include "simplevec.h"
class HAnimClass;
class HTreeClass;
class ChunkLoadClass;
class FileClass;
class FileFactoryClass;
class PrototypeLoaderClass;
class Font3DDataClass;
class Font3DInstanceClass;
class FontCharsClass;
class RenderObjClass;
class HModelClass;
class PrototypeClass;
class HTreeManagerClass;
class HAnimManagerClass;
class HAnimIterator;
class TextureIterator;
class TextureFileCache;
class StreamingTextureClass;
struct StreamingTextureConfig;
class TextureClass;
class MetalMapManagerClass;
/*
** AssetIterator
** This object can iterate through the 3D assets which
** currently exist in the Asset Manager. It tells you the names
** of the assets which the manager can create for you.
*/
class AssetIterator
{
public:
virtual ~AssetIterator(void) { };
virtual void First(void) { Index = 0; }
virtual void Next(void) { Index ++; }
virtual bool Is_Done(void) = 0;
virtual const char * Current_Item_Name(void) = 0;
protected:
AssetIterator(void) { Index = 0; }
int Index;
};
/*
** RenderObjIterator
** The render obj iterator simply adds a method for determining
** the class id of a render object prototype in the system.
*/
class RenderObjIterator : public AssetIterator
{
public:
virtual int Current_Item_Class_ID(void) = 0;
};
/*
WW3DAssetManager
This object is the manager of all of the 3D data. Load your meshes, animations,
etc etc using the Load_3D_Assets function.
WARNING: hierarchy trees should be loaded before the meshes and animations
which attach to them.
-------------------------------------------------------------------------------------
Dec 11, 1997, Asset Manager Brainstorming:
- WW3DAssetManager will be diferentiated from other game data asset managers
(sounds, strings, etc) because they behave differently and serve different
purposes
- WW3D creates "clones" from the blueprints it has of render objects whereas
Our commando data asset manager will provide the data (file images) for the
blueprints. Maybe the CommandoDataManager could deal in MemoryFileClasses.
Or void * and then the ww3d manager could convert to MemoryFiles...
- Future caching: In the case that we want to implement a caching system,
assets must be "released" when not in use.
- CommandoW3d asset manager asks the game data asset manager for assets by name.
Game data manager must have a "directory" structure which maps each named
asset to data on disk. It then returns an image of the file once it has
been loaded into ram.
- Assets must be individual files, named with the asset name used in code/scripting
We will write a tool which chops w3d files up so that all of the individual assets
are brought out into their own file and named with the actual w3d name.
- Data Asset Manager will load the file into ram, give it to us and forget about it
W3d will release_ref it or delete it and the file image will go away.
- Each time the 3d asset manager is requested for an asset, it will look through
the render objects it has, if the asset isn't found, it will ask for the asset
from the generic data asset manager. When creating the actual render object,
the 3d asset manager may find that it needs another asset (such as a hierarchy tree).
It will then recurse, and ask for that asset.
- Copy Mode will go away. It will be internally set based on whether the mesh
contains vertex animation. All other render objects will simply "Clone".
- Commando will derive a Cmdo3DAssetManager which knows about the special chunks
required for the terrains.
-------------------------------------------------------------------------------------
July 28, 1998
- Exposed the prototype system and added prototype loaders (PrototypeClass and
PrototypeLoaderClass in proto.h). This now allows the user to install his own
loaders for new render object types.
- Simplified the interface by removing the special purpose creation functions,
leaving only the Create_Render_Obj function.
- In certain cases some users need to know what kind of render object was created
so we added a Class_ID mechanism to RenderObjClass.
- Class_ID for render objects is not enough. Need the asset iterator to be able
to tell you the Class_ID of each asset in the asset manager. This also means that
the prototype class needs to be able to tell you the class ID. Actually this
code only seems to be used by tools such as SView but is needed anyway...
*/
class WW3DAssetManager
{
public:
/*
** Constructor and destructor
*/
WW3DAssetManager(void);
virtual ~WW3DAssetManager(void);
/*
** Access to the single instance of a WW3DAssetManager. The user
** can subclass their own asset manager class but should only
** create one instance. (a violation of this will be caught with
** a run-time assertion)
**
** The "official" way to get at the global asset manager is to
** use a line of code like this:
** WW3DAssetManager::Get_Instance();
*/
static WW3DAssetManager * Get_Instance(void) { return TheInstance; }
static void Delete_This(void) { if (TheInstance) delete TheInstance; TheInstance=NULL; }
/*
** Load data from any type of w3d file
*/
virtual bool Load_3D_Assets( const char * filename);
virtual bool Load_3D_Assets(FileClass & assetfile);
/*
** Get rid of all of the currently loaded assets
*/
virtual void Free_Assets(void);
/*
** Release any assets that only the asset manager has a reference to.
*/
virtual void Release_Unused_Assets(void);
/*
** Release assets not in the given exclusion list.
*/
virtual void Free_Assets_With_Exclusion_List(const DynamicVectorClass<StringClass> & model_exclusion_list);
virtual void Create_Asset_List(DynamicVectorClass<StringClass> & model_exclusion_list);
/*
** create me an instance of one of the prototype render objects
*/
virtual RenderObjClass * Create_Render_Obj(const char * name);
/*
** query if there is a render object with the specified name
*/
virtual bool Render_Obj_Exists(const char * name);
/*
** Iterate through all render objects or through the
** sub-categories of render objects. NOTE! the user is responsible
** for releasing the iterator when finished with it!
*/
virtual RenderObjIterator * Create_Render_Obj_Iterator(void);
virtual void Release_Render_Obj_Iterator(RenderObjIterator *);
/*
** Access to HAnims, Used by Animatable3DObj's
** TODO: make HAnims accessible from the HMODELS (or Animatable3DObj...)
*/
virtual AssetIterator * Create_HAnim_Iterator(void);
virtual HAnimClass * Get_HAnim(const char * name);
virtual bool Add_Anim (HAnimClass *new_anim) { return HAnimManager.Add_Anim (new_anim); }
/*
** Access to textures
*/
// virtual AssetIterator * Create_Texture_Iterator(void);
HashTemplateClass<StringClass,TextureClass*>& Texture_Hash() { return TextureHash; }
static void Log_Texture_Statistics();
virtual TextureClass * Get_Texture
(
const char * filename,
MipCountType mip_level_count=MIP_LEVELS_ALL,
WW3DFormat texture_format=WW3D_FORMAT_UNKNOWN,
bool allow_compression=true,
TextureBaseClass::TexAssetType type=TextureBaseClass::TEX_REGULAR,
bool allow_reduction=true
);
virtual void Release_All_Textures(void);
virtual void Release_Unused_Textures(void);
virtual void Release_Texture(TextureClass *);
virtual void Load_Procedural_Textures();
virtual MetalMapManagerClass* Peek_Metal_Map_Manager() { return MetalManager; }
/*
** Access to Font3DInstances. (These are not saved, we just use the
** asset manager as a convienient way to create them.)
*/
virtual Font3DInstanceClass * Get_Font3DInstance( const char * name);
/*
** Access to FontChars. Used by Render2DSentenceClass
*/
virtual FontCharsClass * Get_FontChars( const char * name, int point_size, bool is_bold = false );
/*
** Access to HTrees, Used by Animatable3DObj's
*/
virtual AssetIterator * Create_HTree_Iterator(void);
virtual HTreeClass * Get_HTree(const char * name);
/*
** Prototype Loaders, The user can register new loaders here. Note that
** a the pointer to your loader will be stored inside the asset manager.
** For this reason, your loader should be a static or global object.
*/
virtual void Register_Prototype_Loader(PrototypeLoaderClass * loader);
/*
** The Add_Prototype is public so that we can add prototypes for procedurally
** generated objects to the asset manager.
*/
void Add_Prototype(PrototypeClass * newproto);
void Remove_Prototype(PrototypeClass *proto);
void Remove_Prototype(const char *name);
PrototypeClass * Find_Prototype(const char * name);
/*
** Load on Demand
*/
bool Get_WW3D_Load_On_Demand( void ) { return WW3D_Load_On_Demand; }
void Set_WW3D_Load_On_Demand( bool on_off ) { WW3D_Load_On_Demand = on_off; }
/*
** Add fog to objects on load
*/
bool Get_Activate_Fog_On_Load( void ) { return Activate_Fog_On_Load; }
void Set_Activate_Fog_On_Load( bool on_off ) { Activate_Fog_On_Load = on_off; }
// Log texture statistics
void Log_All_Textures();
protected:
/*
** Access to Font3DData. (These are privately managed/accessed)
*/
virtual AssetIterator * Create_Font3DData_Iterator(void);
virtual void Add_Font3DData(Font3DDataClass * font);
virtual void Remove_Font3DData(Font3DDataClass * font);
virtual Font3DDataClass * Get_Font3DData(const char * name);
virtual void Release_All_Font3DDatas( void);
virtual void Release_Unused_Font3DDatas( void);
virtual void Release_All_FontChars( void );
void Free(void);
PrototypeLoaderClass * Find_Prototype_Loader(int chunk_id);
bool Load_Prototype(ChunkLoadClass & cload);
/*
** Compile time control over the dynamic arrays:
*/
enum
{
PROTOLOADERS_VECTOR_SIZE = 32,
PROTOLOADERS_GROWTH_RATE = 16,
PROTOTYPES_VECTOR_SIZE = 256,
PROTOTYPES_GROWTH_RATE = 32,
};
/*
** Prototype Loaders
** These objects are responsible for importing certain W3D chunk types and turning
** them into prototypes.
*/
DynamicVectorClass < PrototypeLoaderClass * > PrototypeLoaders;
/*
** Prototypes
** These objects are abstract factories for named render objects. Prototypes is
** a dynamic array of pointers to the currently loaded prototypes.
*/
DynamicVectorClass < PrototypeClass * > Prototypes;
/*
** Prototype Hash Table
** This structure is simply used to speed up the name lookup for prototypes
*/
enum
{
PROTOTYPE_HASH_TABLE_SIZE = 4096,
PROTOTYPE_HASH_BITS = 12,
PROTOTYPE_HASH_MASK = 0x00000FFF
};
PrototypeClass * * PrototypeHashTable;
/*
** managers of HTrees, HAnims, Textures....
*/
HTreeManagerClass HTreeManager;
HAnimManagerClass HAnimManager;
/*
** When enabled, this handles all the caching for the texture class.
** If NULL then textures are not being cached.
*/
TextureFileCache * TextureCache;
/*
** list of Font3DDatas
*/
SList<Font3DDataClass> Font3DDatas;
/*
** list of FontChars
*/
SimpleDynVecClass<FontCharsClass*> FontCharsList;
/*
** Should .W3D be loaded if not in memory
*/
bool WW3D_Load_On_Demand;
/*
** Should we activate fog on objects while loading them
*/
bool Activate_Fog_On_Load;
// Metal Map Manager
MetalMapManagerClass * MetalManager;
/*
** Texture hash table for quick texture lookups
*/
HashTemplateClass<StringClass, TextureClass *> TextureHash;
/*
** The 3d asset manager is a singleton, there should be only
** one and it is accessible through Get_Instance()
*/
static WW3DAssetManager * TheInstance;
/*
** the iterator classes are friends
*/
friend class RObjIterator;
friend class HAnimIterator;
friend class HTreeIterator;
friend class Font3DDataIterator;
friend class TextureIterator;
// Font3DInstance need access to the Font3DData
friend class Font3DInstanceClass;
};
#endif

View file

@ -0,0 +1,115 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "assetstatus.h"
#include "hashtemplate.h"
#include "wwstring.h"
#include "rawfile.h"
AssetStatusClass AssetStatusClass::Instance;
const char* ReportCategoryNames[AssetStatusClass::REPORT_COUNT]={
"LOAD_ON_DEMAND_ROBJ",
"LOAD_ON_DEMAND_HANIM",
"LOAD_ON_DEMAND_HTREE",
"MISSING_ROBJ",
"MISSING_HANIM",
"MISSING_HTREE"
};
AssetStatusClass::AssetStatusClass()
:
Reporting (true),
LoadOnDemandReporting(false)
{
}
AssetStatusClass::~AssetStatusClass()
{
#ifdef WWDEBUG
if (Reporting) {
StringClass report("Load-on-demand and missing assets report\r\n\r\n");
for (int i=0;i<REPORT_COUNT;++i) {
report+="Category: ";
report+=ReportCategoryNames[i];
report+="\r\n\r\n";
HashTemplateIterator<StringClass,int> ite(ReportHashTables[i]);
for (ite.First();!ite.Is_Done();ite.Next()) {
report+=ite.Peek_Key();
int count=ite.Peek_Value();
if (count>1) {
StringClass tmp(0,true);
tmp.Format("\t(reported %d times)",count);
report+=tmp;
}
report+="\r\n";
}
report+="\r\n";
}
if (report.Get_Length()) {
RawFileClass raw_log_file("asset_report.txt");
raw_log_file.Create();
raw_log_file.Open(RawFileClass::WRITE);
raw_log_file.Write(report,report.Get_Length());
raw_log_file.Close();
}
}
#endif
}
void AssetStatusClass::Add_To_Report(int index, const char* name)
{
StringClass lower_case_name(name,true);
_strlwr(lower_case_name.Peek_Buffer());
// This is a bit slow - two accesses to the same member, but currently there's no better way to do it.
int count=ReportHashTables[index].Get(lower_case_name);
count++;
ReportHashTables[index].Set_Value(lower_case_name,count);
}
void AssetStatusClass::Report_Load_On_Demand_RObj(const char* name)
{
if (LoadOnDemandReporting) Add_To_Report(REPORT_LOAD_ON_DEMAND_ROBJ,name);
}
void AssetStatusClass::Report_Load_On_Demand_HAnim(const char* name)
{
if (LoadOnDemandReporting) Add_To_Report(REPORT_LOAD_ON_DEMAND_HANIM,name);
}
void AssetStatusClass::Report_Load_On_Demand_HTree(const char* name)
{
if (LoadOnDemandReporting) Add_To_Report(REPORT_LOAD_ON_DEMAND_HTREE,name);
}
void AssetStatusClass::Report_Missing_RObj(const char* name)
{
Add_To_Report(REPORT_MISSING_ROBJ,name);
}
void AssetStatusClass::Report_Missing_HAnim(const char* name)
{
Add_To_Report(REPORT_MISSING_HANIM,name);
}
void AssetStatusClass::Report_Missing_HTree(const char* name)
{
Add_To_Report(REPORT_MISSING_HTREE,name);
}

View file

@ -0,0 +1,68 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef WW3D_ASSET_STATUS_H
#define WW3D_ASSET_STATUS_H
#include "always.h"
#include "hashtemplate.h"
class AssetStatusClass
{
public:
enum {
REPORT_LOAD_ON_DEMAND_ROBJ,
REPORT_LOAD_ON_DEMAND_HANIM,
REPORT_LOAD_ON_DEMAND_HTREE,
REPORT_MISSING_ROBJ,
REPORT_MISSING_HANIM,
REPORT_MISSING_HTREE,
REPORT_COUNT
};
AssetStatusClass();
~AssetStatusClass();
void Enable_Reporting(bool enable) { Reporting=enable; }
void Enable_Load_On_Demand_Reporting(bool enable) { LoadOnDemandReporting=enable; }
void Report_Load_On_Demand_RObj(const char* name);
void Report_Load_On_Demand_HAnim(const char* name);
void Report_Load_On_Demand_HTree(const char* name);
void Report_Missing_RObj(const char* name);
void Report_Missing_HAnim(const char* name);
void Report_Missing_HTree(const char* name);
static AssetStatusClass* Peek_Instance() { return &Instance; }
private:
bool Reporting;
bool LoadOnDemandReporting;
static AssetStatusClass Instance;
HashTemplateClass<StringClass, int> ReportHashTables[REPORT_COUNT];
void Add_To_Report(int index, const char* name);
};
#endif

View file

@ -0,0 +1,448 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bitmaphandler.h"
#include "wwdebug.h"
#include "colorspace.h"
void Bitmap_Assert(bool condition)
{
WWASSERT(condition);
}
void BitmapHandlerClass::Create_Mipmap_B8G8R8A8(
unsigned char* dest_surface,
unsigned dest_surface_pitch,
unsigned char* src_surface,
unsigned src_surface_pitch,
unsigned width,
unsigned height)
{
unsigned src_pitch=src_surface_pitch/4;
for (unsigned y=0;y<height;y+=2) {
unsigned* dest=(unsigned*)dest_surface;
dest_surface+=dest_surface_pitch;
unsigned* src=(unsigned*)src_surface;
src_surface+=src_surface_pitch;
for (unsigned x=0;x<width;x+=2) {
unsigned bgra3=src[src_pitch];
unsigned bgra1=*src++;
unsigned bgra4=src[src_pitch];
unsigned bgra2=*src++;
*dest++=Combine_A8R8G8B8(bgra1,bgra2,bgra3,bgra4);
}
}
}
void BitmapHandlerClass::Copy_Image_Generate_Mipmap(
unsigned width,
unsigned height,
unsigned char* dest_surface,
unsigned dest_pitch,
WW3DFormat dest_format,
unsigned char* src_surface,
unsigned src_pitch,
WW3DFormat src_format,
unsigned char* mip_surface,
unsigned mip_pitch,
const Vector3& hsv_shift)
{
// Optimized loop if source and destination are 32 bit
bool has_hsv_shift = hsv_shift[0]!=0.0f || hsv_shift[1]!=0.0f || hsv_shift[2]!=0.0f;
if (src_format==dest_format && src_format==WW3D_FORMAT_A8R8G8B8) {
dest_pitch/=4;
src_pitch/=4;
mip_pitch/=4;
for (unsigned y=0;y<height/2;++y) {
unsigned* dest_ptr=(unsigned*)dest_surface;
dest_ptr+=2*y*dest_pitch;
unsigned* src_ptr=(unsigned*)src_surface;
src_ptr+=y*2*src_pitch;
unsigned* mip_ptr=(unsigned*)mip_surface;
mip_ptr+=y*mip_pitch;
unsigned b8g8r8a8_00;
unsigned b8g8r8a8_01;
unsigned b8g8r8a8_10;
unsigned b8g8r8a8_11;
for (unsigned x=0;x<width/2;x++) {
b8g8r8a8_10=src_ptr[src_pitch];
dest_ptr[dest_pitch]=b8g8r8a8_10;
b8g8r8a8_00=*src_ptr++;
*dest_ptr++=b8g8r8a8_00;
b8g8r8a8_11=src_ptr[src_pitch];
dest_ptr[dest_pitch]=b8g8r8a8_11;
b8g8r8a8_01=*src_ptr++;
*dest_ptr++=b8g8r8a8_01;
unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
if (has_hsv_shift) {
Recolor(b8g8r8a8,hsv_shift);
}
*mip_ptr++=b8g8r8a8;
}
}
return;
}
WWASSERT(src_format!=WW3D_FORMAT_P8); // This function doesn't support paletted formats
unsigned src_bpp=Get_Bytes_Per_Pixel(src_format);
unsigned dest_bpp=Get_Bytes_Per_Pixel(dest_format);
for (unsigned y=0;y<height/2;++y) {
unsigned char* dest_ptr=dest_surface+2*y*dest_pitch;
unsigned char* src_ptr=src_surface+y*2*src_pitch;
unsigned char* mip_ptr=mip_surface+y*mip_pitch;
unsigned b8g8r8a8_00;
unsigned b8g8r8a8_01;
unsigned b8g8r8a8_10;
unsigned b8g8r8a8_11;
for (unsigned x=0;x<width/2;x++,dest_ptr+=dest_bpp*2,src_ptr+=src_bpp*2,mip_ptr+=dest_bpp) {
Read_B8G8R8A8(b8g8r8a8_00,src_ptr,src_format,NULL,0);
Write_B8G8R8A8(dest_ptr,dest_format,b8g8r8a8_00);
Read_B8G8R8A8(b8g8r8a8_01,src_ptr+src_bpp,src_format,NULL,0);
Write_B8G8R8A8(dest_ptr+dest_bpp,dest_format,b8g8r8a8_01);
Read_B8G8R8A8(b8g8r8a8_10,src_ptr+src_pitch,src_format,NULL,0);
Write_B8G8R8A8(dest_ptr+dest_pitch,dest_format,b8g8r8a8_10);
Read_B8G8R8A8(b8g8r8a8_11,src_ptr+src_bpp+src_pitch,src_format,NULL,0);
Write_B8G8R8A8(dest_ptr+dest_bpp+dest_pitch,dest_format,b8g8r8a8_11);
unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
if (has_hsv_shift) {
Recolor(b8g8r8a8,hsv_shift);
}
Write_B8G8R8A8(mip_ptr,dest_format,b8g8r8a8);
}
}
}
// ----------------------------------------------------------------------------
//
// Copy image from source surface to destination surface with stretch and color
// space conversion if needed. If 'generate_mip_level' is set, process image
// in 2x2 blocks and generate mipmap on top of the original source image while
// copying.
//
// ----------------------------------------------------------------------------
void BitmapHandlerClass::Copy_Image(
unsigned char* dest_surface,
unsigned dest_surface_width,
unsigned dest_surface_height,
unsigned dest_surface_pitch,
WW3DFormat dest_surface_format,
unsigned char* src_surface,
unsigned src_surface_width,
unsigned src_surface_height,
unsigned src_surface_pitch,
WW3DFormat src_surface_format,
const unsigned char* src_palette,
unsigned src_palette_bpp,
bool generate_mip_level,
const Vector3& hsv_shift)
{
WWASSERT(dest_surface_width);
WWASSERT(dest_surface_height);
// Bumpmap?
if (dest_surface_format==WW3D_FORMAT_U8V8 ||
dest_surface_format==WW3D_FORMAT_L6V5U5 ||
dest_surface_format==WW3D_FORMAT_X8L8V8U8) {
unsigned src_bpp=Get_Bytes_Per_Pixel(src_surface_format);
for( unsigned y=0; y<dest_surface_height; y++ ) {
unsigned char* dest_ptr=dest_surface;
dest_ptr+=y*dest_surface_pitch;
unsigned char* src_ptr_mid=src_surface;
src_ptr_mid+=y*src_surface_pitch;
unsigned char* src_ptr_next_line = ( src_ptr_mid + src_surface_pitch );
unsigned char* src_ptr_prev_line = ( src_ptr_mid - src_surface_pitch );
if( y == src_surface_height-1 ) // Don't go past the last line
src_ptr_next_line = src_ptr_mid;
if( y == 0 ) // Don't go before first line
src_ptr_prev_line = src_ptr_mid;
for( unsigned x=0; x<dest_surface_width; x++ ) {
unsigned pixel00;
unsigned pixel01;
unsigned pixelM1;
unsigned pixel10;
unsigned pixel1M;
Read_B8G8R8A8(pixel00,src_ptr_mid,src_surface_format,NULL,0);
Read_B8G8R8A8(pixel01,src_ptr_mid+src_bpp,src_surface_format,NULL,0);
Read_B8G8R8A8(pixelM1,src_ptr_mid-src_bpp,src_surface_format,NULL,0);
Read_B8G8R8A8(pixel10,src_ptr_prev_line,src_surface_format,NULL,0);
Read_B8G8R8A8(pixel1M,src_ptr_next_line,src_surface_format,NULL,0);
// Convert to luminance
unsigned char bv00;
unsigned char bv01;
unsigned char bvM1;
unsigned char bv10;
unsigned char bv1M;
Write_B8G8R8A8(&bv00,WW3D_FORMAT_L8,pixel00);
Write_B8G8R8A8(&bv01,WW3D_FORMAT_L8,pixel01);
Write_B8G8R8A8(&bvM1,WW3D_FORMAT_L8,pixelM1);
Write_B8G8R8A8(&bv10,WW3D_FORMAT_L8,pixel10);
Write_B8G8R8A8(&bv1M,WW3D_FORMAT_L8,pixel1M);
int v00=bv00,v01=bv01,vM1=bvM1,v10=bv10,v1M=bv1M;
int iDu = (vM1-v01); // The delta-u bump value
int iDv = (v1M-v10); // The delta-v bump value
if( (v00 < vM1) && (v00 < v01) ) { // If we are at valley
iDu = vM1-v00; // Choose greater of 1st order diffs
if( iDu < v00-v01 )
iDu = v00-v01;
}
// The luminance bump value (land masses are less shiny)
unsigned short uL = ( v00>1 ) ? 63 : 127;
switch(dest_surface_format) {
case WW3D_FORMAT_U8V8:
*dest_ptr++ = (unsigned char)iDu;
*dest_ptr++ = (unsigned char)iDv;
break;
case WW3D_FORMAT_L6V5U5:
*(unsigned short*)dest_ptr = (unsigned short)( ( (iDu>>3) & 0x1f ) << 0 );
*(unsigned short*)dest_ptr |= (unsigned short)( ( (iDv>>3) & 0x1f ) << 5 );
*(unsigned short*)dest_ptr |= (unsigned short)( ( ( uL>>2) & 0x3f ) << 10 );
dest_ptr += 2;
break;
case WW3D_FORMAT_X8L8V8U8:
*dest_ptr++ = (unsigned char)iDu;
*dest_ptr++ = (unsigned char)iDv;
*dest_ptr++ = (unsigned char)uL;
*dest_ptr++ = (unsigned char)0L;
break;
default:
WWASSERT(0); // Unknown bumpmap format
break;
}
// Move one pixel to the left (src is 32-bpp)
src_ptr_mid+=src_bpp;
src_ptr_prev_line+=src_bpp;
src_ptr_next_line+=src_bpp;
}
}
return;
}
bool has_hsv_shift = hsv_shift[0]!=0.0f || hsv_shift[1]!=0.0f || hsv_shift[2]!=0.0f;
if (src_surface_format==dest_surface_format && (src_surface_format==WW3D_FORMAT_A8R8G8B8 || src_surface_format==WW3D_FORMAT_X8R8G8B8)) {
// One-to-one copy or scaling?
dest_surface_pitch/=4;
src_surface_pitch/=4;
if (dest_surface_width==src_surface_width && dest_surface_height==src_surface_height) {
// Generate the next mip level while copying the current surface?
if (generate_mip_level) {
if (dest_surface_width==1) {
unsigned b8g8r8a8=*(unsigned*)src_surface;
if (has_hsv_shift) Recolor(b8g8r8a8,hsv_shift);
*(unsigned*)dest_surface=b8g8r8a8;
}
else {
for (unsigned y=0;y<dest_surface_height/2;++y) {
unsigned* dest_ptr=(unsigned*)dest_surface;
dest_ptr+=2*y*dest_surface_pitch;
unsigned* src_ptr=(unsigned*)src_surface;
unsigned* mip_ptr=src_ptr;
src_ptr+=y*2*src_surface_pitch;
mip_ptr+=y*src_surface_pitch;
unsigned b8g8r8a8_00;
unsigned b8g8r8a8_01;
unsigned b8g8r8a8_10;
unsigned b8g8r8a8_11;
for (unsigned x=0;x<dest_surface_width/2;x++) {
// Read four pixels from the source
b8g8r8a8_10=src_ptr[src_surface_pitch];
b8g8r8a8_00=*src_ptr++;
b8g8r8a8_11=src_ptr[src_surface_pitch];
b8g8r8a8_01=*src_ptr++;
// Recolor if necessary
if (has_hsv_shift) {
Recolor(b8g8r8a8_00,hsv_shift);
Recolor(b8g8r8a8_01,hsv_shift);
Recolor(b8g8r8a8_10,hsv_shift);
Recolor(b8g8r8a8_11,hsv_shift);
}
// Write the four pixels to the destination
dest_ptr[dest_surface_pitch]=b8g8r8a8_10;
*dest_ptr++=b8g8r8a8_00;
dest_ptr[dest_surface_pitch]=b8g8r8a8_11;
*dest_ptr++=b8g8r8a8_01;
// Write combined four pixels to the destination mip map level
*mip_ptr++=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
}
}
}
}
else {
for (unsigned y=0;y<dest_surface_height;++y) {
unsigned* dest_ptr=(unsigned*)dest_surface;
dest_ptr+=y*dest_surface_pitch;
const unsigned* src_ptr=(unsigned*)src_surface;
src_ptr+=y*src_surface_pitch;
if (has_hsv_shift) {
for (unsigned x=0;x<dest_surface_width;++x) {
unsigned b8g8r8a8=*src_ptr++;
Recolor(b8g8r8a8,hsv_shift);
*dest_ptr++=b8g8r8a8;
}
}
else {
for (unsigned x=0;x<dest_surface_width;++x) {
*dest_ptr++=*src_ptr++;
}
}
}
}
}
else {
// For now do only point-sampling
for (unsigned y=0;y<dest_surface_height;++y) {
unsigned* dest_ptr=(unsigned*)dest_surface;
dest_ptr+=y*dest_surface_pitch;
unsigned src_y=y*src_surface_height/dest_surface_height;
const unsigned* src_ptr=(unsigned*)src_surface;
src_ptr+=src_y*src_surface_pitch;
for (unsigned x=0;x<dest_surface_width;++x) {
unsigned src_x=x*src_surface_width/dest_surface_width;
unsigned b8g8r8a8=src_ptr[src_x];
if (has_hsv_shift) {
Recolor(b8g8r8a8,hsv_shift);
}
*dest_ptr++=b8g8r8a8;
}
}
}
return;
}
unsigned dest_bpp=Get_Bytes_Per_Pixel(dest_surface_format);
unsigned src_bpp=Get_Bytes_Per_Pixel(src_surface_format);
// One-to-one copy or scaling?
if (dest_surface_width==src_surface_width && dest_surface_height==src_surface_height) {
// Generate the next mip level while copying the current surface?
if (generate_mip_level) {
WWASSERT(src_surface_format!=WW3D_FORMAT_P8); // Paletted textures can't be mipmapped
if (dest_surface_width==1) {
unsigned char* dest_ptr=dest_surface;
unsigned char* src_ptr=src_surface;
unsigned b8g8r8a8;
Read_B8G8R8A8(b8g8r8a8,src_ptr,src_surface_format,src_palette,src_palette_bpp);
if (has_hsv_shift) {
Recolor(b8g8r8a8,hsv_shift);
}
Write_B8G8R8A8(dest_ptr,dest_surface_format,b8g8r8a8);
}
else {
for (unsigned y=0;y<dest_surface_height/2;++y) {
unsigned char* dest_ptr=dest_surface+2*y*dest_surface_pitch;
unsigned char* src_ptr=src_surface+y*2*src_surface_pitch;
unsigned char* mip_ptr=src_surface+y*src_surface_pitch;
unsigned b8g8r8a8_00;
unsigned b8g8r8a8_01;
unsigned b8g8r8a8_10;
unsigned b8g8r8a8_11;
for (unsigned x=0;x<dest_surface_width/2;x++,dest_ptr+=dest_bpp*2,src_ptr+=src_bpp*2,mip_ptr+=src_bpp) {
// Read four pixels from the source
Read_B8G8R8A8(b8g8r8a8_00,src_ptr,src_surface_format,src_palette,src_palette_bpp);
Read_B8G8R8A8(b8g8r8a8_01,src_ptr+src_bpp,src_surface_format,src_palette,src_palette_bpp);
Read_B8G8R8A8(b8g8r8a8_10,src_ptr+src_surface_pitch,src_surface_format,src_palette,src_palette_bpp);
Read_B8G8R8A8(b8g8r8a8_11,src_ptr+src_bpp+src_surface_pitch,src_surface_format,src_palette,src_palette_bpp);
// Recolor if necessary
if (has_hsv_shift) {
Recolor(b8g8r8a8_00,hsv_shift);
Recolor(b8g8r8a8_01,hsv_shift);
Recolor(b8g8r8a8_10,hsv_shift);
Recolor(b8g8r8a8_11,hsv_shift);
}
// Write the four pixels to the destination
Write_B8G8R8A8(dest_ptr,dest_surface_format,b8g8r8a8_00);
Write_B8G8R8A8(dest_ptr+dest_bpp,dest_surface_format,b8g8r8a8_01);
Write_B8G8R8A8(dest_ptr+dest_surface_pitch,dest_surface_format,b8g8r8a8_10);
Write_B8G8R8A8(dest_ptr+dest_bpp+dest_surface_pitch,dest_surface_format,b8g8r8a8_11);
// Write combined four pixels to the destination mip map level
unsigned b8g8r8a8=Combine_A8R8G8B8(b8g8r8a8_00,b8g8r8a8_01,b8g8r8a8_10,b8g8r8a8_11);
Write_B8G8R8A8(mip_ptr,src_surface_format,b8g8r8a8);
}
}
}
}
else {
for (unsigned y=0;y<dest_surface_height;++y) {
unsigned char* dest_ptr=dest_surface+y*dest_surface_pitch;
const unsigned char* src_ptr=src_surface+y*src_surface_pitch;
if (has_hsv_shift) {
for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp,src_ptr+=src_bpp) {
Copy_Pixel(dest_ptr,dest_surface_format,src_ptr,src_surface_format,src_palette,src_palette_bpp,hsv_shift);
}
}
else {
for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp,src_ptr+=src_bpp) {
Copy_Pixel(dest_ptr,dest_surface_format,src_ptr,src_surface_format,src_palette,src_palette_bpp);
}
}
}
}
}
else {
// For now do only point-sampling
for (unsigned y=0;y<dest_surface_height;++y) {
unsigned char* dest_ptr=dest_surface+y*dest_surface_pitch;
unsigned src_y=y*src_surface_height/dest_surface_height;
const unsigned char* src_ptr=src_surface+src_y*src_surface_pitch;
if (has_hsv_shift) {
for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp) {
unsigned src_x=x*src_surface_width/dest_surface_width;
src_x*=src_bpp;
Copy_Pixel(dest_ptr,dest_surface_format,src_ptr+src_x,src_surface_format,src_palette,src_palette_bpp,hsv_shift);
}
}
else {
for (unsigned x=0;x<dest_surface_width;++x,dest_ptr+=dest_bpp) {
unsigned src_x=x*src_surface_width/dest_surface_width;
src_x*=src_bpp;
Copy_Pixel(dest_ptr,dest_surface_format,src_ptr+src_x,src_surface_format,src_palette,src_palette_bpp);
}
}
}
}
}

View file

@ -0,0 +1,484 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef BITMAPHANDLER_H
#define BITMAPHANDLER_H
#include "always.h"
#include "ww3dformat.h"
#include "vector3.h"
#include "colorspace.h"
void Bitmap_Assert(bool condition);
class BitmapHandlerClass
{
public:
// Read pixel at given address
WWINLINE static void Read_B8G8R8A8(
unsigned char* argb,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp);
// Read pixel at given address
WWINLINE static void Read_B8G8R8A8(
unsigned& argb,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp);
// Read pixel from surface
WWINLINE static void Read_B8G8R8A8(
unsigned& argb,
const unsigned char* src_ptr,
WW3DFormat src_format,
int x,
int y,
int width,
int height,
const unsigned char* palette,
unsigned palette_bpp);
WWINLINE static void Write_B8G8R8A8(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned char* argb);
WWINLINE static void Write_B8G8R8A8(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned& argb);
WWINLINE static void Copy_Pixel(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp);
WWINLINE static void Copy_Pixel(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp,
const Vector3& hsv_shift);
WWINLINE static unsigned Combine_A8R8G8B8(
unsigned bgra1,
unsigned bgra2,
unsigned bgra3,
unsigned bgra4);
static void Create_Mipmap_B8G8R8A8(
unsigned char* dest_surface,
unsigned dest_surface_pitch,
unsigned char* src_surface,
unsigned src_surface_pitch,
unsigned width,
unsigned height);
static void Copy_Image_Generate_Mipmap(
unsigned width,
unsigned height,
unsigned char* dest_surface,
unsigned dest_pitch,
WW3DFormat dest_format,
unsigned char* src_surface,
unsigned src_pitch,
WW3DFormat src_format,
unsigned char* mip_surface,
unsigned mip_pitch,
const Vector3& hsv_shift=Vector3(0.0f,0.0f,0.0f));
static void Copy_Image(
unsigned char* dest_surface,
unsigned dest_surface_width,
unsigned dest_surface_height,
unsigned dest_surface_pitch,
WW3DFormat dest_surface_format,
unsigned char* src_surface,
unsigned src_surface_width,
unsigned src_surface_height,
unsigned src_surface_pitch,
WW3DFormat src_surface_format,
const unsigned char* src_palette,
unsigned src_palette_bpp,
bool generate_mip_level,
const Vector3& hsv_shift=Vector3(0.0f,0.0f,0.0f));
};
// ----------------------------------------------------------------------------
//
// Read color value of given type in BGRA (D3D) byte order. Regarless
// of the source format the color value is converted to 32-bit format.
//
// ----------------------------------------------------------------------------
WWINLINE void BitmapHandlerClass::Read_B8G8R8A8(
unsigned char* argb,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp)
{
switch (src_format) {
case WW3D_FORMAT_A8R8G8B8:
case WW3D_FORMAT_X8R8G8B8:
*(unsigned*)argb=*(unsigned*)src_ptr;
break;
case WW3D_FORMAT_R8G8B8:
*argb++=src_ptr[0];
*argb++=src_ptr[1];
*argb++=src_ptr[2];
*argb++=0xff;
break;
case WW3D_FORMAT_A4R4G4B4:
{
unsigned short tmp;
tmp=*(unsigned short*)src_ptr;
*argb++=((tmp&0x000f)<<4);
*argb++=((tmp&0x00f0));
*argb++=((tmp&0x0f00)>>4);
*argb++=((tmp&0xf000)>>8);
}
break;
case WW3D_FORMAT_A1R5G5B5:
{
unsigned short tmp;
tmp=*(unsigned short*)src_ptr;
argb[3]=tmp&0x8000 ? 0xff : 0x0;
argb[2]=(tmp>>7)&0xf8;
argb[1]=(tmp>>2)&0xf8;
argb[0]=(tmp<<3)&0xf8;
}
break;
case WW3D_FORMAT_R5G6B5:
{
unsigned short tmp;
tmp=*(unsigned short*)src_ptr;
argb[3]=0xff;
argb[2]=(tmp>>8)&0xf8;
argb[1]=(tmp>>3)&0xfc;
argb[0]=(tmp<<3)&0xf8;
}
break;
case WW3D_FORMAT_R3G3B2:
{
unsigned char tmp=*src_ptr;
argb[3]=0xff;
argb[2]=tmp&0xe0;
argb[1]=(tmp<<3)&0xe0;
argb[0]=(tmp<<6)&0xc0;
}
break;
case WW3D_FORMAT_L8:
{
unsigned char tmp=*src_ptr++;
*argb++=tmp;
*argb++=tmp;
*argb++=tmp;
*argb++=0xff;
}
break;
case WW3D_FORMAT_A8:
{
*argb++=0;
*argb++=0;
*argb++=0;
*argb++=*src_ptr++;
}
break;
case WW3D_FORMAT_P8:
{
unsigned char index=*src_ptr++;
switch (palette_bpp) {
case 4:
*argb++=palette[palette_bpp*index+3];
*argb++=palette[palette_bpp*index+2];
*argb++=palette[palette_bpp*index+1];
*argb++=palette[palette_bpp*index+0];
break;
case 3:
*argb++=palette[palette_bpp*index+2];
*argb++=palette[palette_bpp*index+1];
*argb++=palette[palette_bpp*index+0];
*argb++=0xff;
break;
case 2:
case 1:
default:
Bitmap_Assert(0);
break;
}
}
break;
case WW3D_FORMAT_DXT1:
case WW3D_FORMAT_DXT2:
case WW3D_FORMAT_DXT3:
case WW3D_FORMAT_DXT4:
case WW3D_FORMAT_DXT5:
default: Bitmap_Assert(0); break;
}
}
WWINLINE void BitmapHandlerClass::Read_B8G8R8A8(
unsigned& argb,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp)
{
Read_B8G8R8A8((unsigned char*)&argb,src_ptr,src_format,palette,palette_bpp);
}
// Read pixel from surface
WWINLINE void BitmapHandlerClass::Read_B8G8R8A8(
unsigned& argb,
const unsigned char* src_ptr,
WW3DFormat src_format,
int x,
int y,
int width,
int height,
const unsigned char* palette,
unsigned palette_bpp)
{
if (x<0 || y<0 || x>=width || y>=height) {
argb=0;
return;
}
unsigned bpp=Get_Bytes_Per_Pixel(src_format);
Read_B8G8R8A8(
argb,
src_ptr+bpp*x+width*bpp*y,
src_format,
palette,
palette_bpp);
}
// ----------------------------------------------------------------------------
//
// Write color value of given type in BGRA (D3D) byte order. The source value
// is always 32 bit and it is converted to defined destination format.
//
// ----------------------------------------------------------------------------
WWINLINE void BitmapHandlerClass::Write_B8G8R8A8(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned char* argb)
{
switch (dest_format) {
case WW3D_FORMAT_A8R8G8B8:
case WW3D_FORMAT_X8R8G8B8:
*(unsigned*)dest_ptr=*(unsigned*)argb;
break;
case WW3D_FORMAT_R8G8B8:
*dest_ptr++=*argb++;
*dest_ptr++=*argb++;
*dest_ptr++=*argb++;
break;
case WW3D_FORMAT_A4R4G4B4:
{
unsigned short tmp;
tmp=((argb[3])&0xf0)<<8;
tmp|=((argb[2])&0xf0)<<4;
tmp|=((argb[1])&0xf0);
tmp|=((argb[0])&0xf0)>>4;
*(unsigned short*)dest_ptr=tmp;
}
break;
case WW3D_FORMAT_A1R5G5B5:
{
unsigned short tmp;
tmp=argb[3] ? 0x8000 : 0x0;
tmp|=((argb[2])&0xf8)<<7;
tmp|=((argb[1])&0xf8)<<2;
tmp|=((argb[0])&0xf8)>>3;
*(unsigned short*)dest_ptr=tmp;
}
break;
case WW3D_FORMAT_R5G6B5:
{
unsigned short tmp;
tmp=((argb[2])&0xf8)<<8;
tmp|=((argb[1])&0xfc)<<3;
tmp|=((argb[0])&0xf8)>>3;
*(unsigned short*)dest_ptr=tmp;
}
break;
case WW3D_FORMAT_R3G3B2:
{
unsigned char tmp;
tmp=((argb[2])&0xe0);
tmp|=((argb[1])&0xe0)>>3;
tmp|=((argb[0])&0xc0)>>6;
*(unsigned short*)dest_ptr=tmp;
}
break;
case WW3D_FORMAT_L8:
{
// CIE Req. 709: Y709 = 0.2125R + 0.7154G + 0.0721B
unsigned char tmp = (unsigned char) ( (
((unsigned int)argb[0] * (unsigned int)0x1275) + // 0.0721B
((unsigned int)argb[1] * (unsigned int)0xB725) + // 0.7154G (rounded up so FF, FF, FF becomes FF)
((unsigned int)argb[2] * (unsigned int)0x3666) // 0.2125R
) >> 16);
*dest_ptr++=tmp;
}
break;
case WW3D_FORMAT_A8:
{
*dest_ptr++=*argb++;
}
break;
case WW3D_FORMAT_DXT1:
case WW3D_FORMAT_DXT2:
case WW3D_FORMAT_DXT3:
case WW3D_FORMAT_DXT4:
case WW3D_FORMAT_DXT5:
case WW3D_FORMAT_P8: // Paletted destination not supported
default: Bitmap_Assert(0); break;
}
}
WWINLINE void BitmapHandlerClass::Write_B8G8R8A8(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned& argb)
{
Write_B8G8R8A8(dest_ptr,dest_format,(unsigned char*)&argb);
}
// ----------------------------------------------------------------------------
//
// Copy pixel. Perform color space conversion if needed. The source and
// destination are always D3D-style BGRA.
//
// ----------------------------------------------------------------------------
WWINLINE void BitmapHandlerClass::Copy_Pixel(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp)
{
// Color space conversion needed?
if (dest_format==src_format) {
switch (dest_format) {
case WW3D_FORMAT_A8R8G8B8:
case WW3D_FORMAT_X8R8G8B8:
*(unsigned*)dest_ptr=*(unsigned*)src_ptr;
break;
case WW3D_FORMAT_R8G8B8:
*dest_ptr++=src_ptr[0];
*dest_ptr++=src_ptr[1];
*dest_ptr++=src_ptr[2];
break;
case WW3D_FORMAT_A4R4G4B4:
{
unsigned short tmp=*(unsigned short*)src_ptr;
*(unsigned short*)dest_ptr=((tmp&0x000f)<<12)|((tmp&0x00f0)<<4)|((tmp&0x0f00)>>4)|((tmp&0xf000)>>12);
}
break;
case WW3D_FORMAT_A1R5G5B5:
{
unsigned short tmp=*(unsigned short*)src_ptr;
*(unsigned short*)dest_ptr=((tmp&0x001f)<<11)|((tmp&0x03e0)<<1)|((tmp&0x7c00)>>9)|((tmp&0x8000)>>15);
}
break;
case WW3D_FORMAT_R5G6B5:
{
unsigned short tmp=*(unsigned short*)src_ptr;
*(unsigned short*)dest_ptr=((tmp&0x001f)<<11)|(tmp&0x07e0)|((tmp&0xf800)>>11);
}
break;
case WW3D_FORMAT_R3G3B2:
case WW3D_FORMAT_L8:
case WW3D_FORMAT_A8: *dest_ptr++=*src_ptr++;
break;
case WW3D_FORMAT_P8: // Paletted destinations not supported
default: Bitmap_Assert(0); break;
}
}
else {
unsigned b8g8r8a8;
Read_B8G8R8A8(b8g8r8a8,src_ptr,src_format,palette,palette_bpp);
Write_B8G8R8A8(dest_ptr,dest_format,b8g8r8a8);
}
}
// ----------------------------------------------------------------------------
//
// Copy pixel with HSV shift. The source and destination are always D3D-style BGRA.
//
// ----------------------------------------------------------------------------
WWINLINE void BitmapHandlerClass::Copy_Pixel(
unsigned char* dest_ptr,
WW3DFormat dest_format,
const unsigned char* src_ptr,
WW3DFormat src_format,
const unsigned char* palette,
unsigned palette_bpp,
const Vector3& hsv_shift)
{
unsigned b8g8r8a8;
Read_B8G8R8A8(b8g8r8a8,src_ptr,src_format,palette,palette_bpp);
Recolor(b8g8r8a8,hsv_shift);
Write_B8G8R8A8(dest_ptr,dest_format,b8g8r8a8);
}
WWINLINE unsigned BitmapHandlerClass::Combine_A8R8G8B8(
unsigned bgra1,
unsigned bgra2,
unsigned bgra3,
unsigned bgra4)
{
bgra1&=0xfcfcfcfc;
bgra2&=0xfcfcfcfc;
bgra3&=0xfcfcfcfc;
bgra4&=0xfcfcfcfc;
bgra1>>=2;
bgra2>>=2;
bgra3>>=2;
bgra4>>=2;
bgra1+=bgra2;
bgra3+=bgra4;
bgra1+=bgra3;
return bgra1;
}
#endif

View file

@ -0,0 +1,299 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***************************************************************************
* *
* Project Name : Commando/G *
* *
* $Archive:: /Commando/Code/ww3d2/bmp2d.cpp $*
* *
* $Org Author:: Jani_p $*
* *
* $Author:: Kenny_m $*
* *
* $Modtime:: 08/05/02 10:44a $*
* *
* $Revision:: 12 $*
* *
* 08/05/02 KM Texture class redesign
*-------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "bmp2d.h"
#include "pot.h"
#include "ww3d.h"
#include "texture.h"
#include "surfaceclass.h"
#include "assetmgr.h"
#include "textureloader.h"
#include "ww3dformat.h"
Bitmap2DObjClass::Bitmap2DObjClass
(
const char *filename,
float screen_x,
float screen_y,
bool center,
bool additive,
bool colorizable,
int usable_width,
int usable_height,
bool ignore_alpha
)
: DynamicScreenMeshClass(2, 4)
{
int resw, resh, resbits;
bool windowed;
// find the resolution (for centering and pixel to pixel translation)
WW3D::Get_Device_Resolution(resw, resh, resbits, windowed);
// This should be the correct way to do things
// but other code expects an aspect ratio of 1.0
// Hector Yee 2/22/01
// Set_Aspect(resh/(float)resw);
// load up the surfaces file name
TextureClass *tex = WW3DAssetManager::Get_Instance()->Get_Texture(filename, MIP_LEVELS_1);
if (!tex->Is_Initialized())
TextureLoader::Request_Foreground_Loading(tex);
SurfaceClass *surface = tex->Get_Surface_Level(0);
if (!surface) {
surface = NEW_REF(SurfaceClass, (32, 32, Get_Valid_Texture_Format(WW3D_FORMAT_R8G8B8,true)));
}
SurfaceClass::SurfaceDescription sd;
surface->Get_Description(sd);
if (usable_width == -1)
usable_width = sd.Width;
if (usable_height == -1)
usable_height = sd.Height;
// if we requested the image to be centered around a point adjust the
// coordinates accordingly.
if (center) {
screen_x -= ((float)usable_width / resw) / 2;
screen_y -= ((float)usable_height / resh) / 2;
}
// The image will be broken down into square textures. The size of these
// textures will be the smallest POT (power of two) which is equal or
// greater than the smaller dimension of the image. Also, the pieces can
// never be larger than 256 texels because some rendering devices don't
// support textures larger than that.
int surf_w = usable_width;
int surf_h = usable_height;
int piece = Find_POT(MIN(surf_w, surf_h));
piece = MIN(piece, 256);
// now take the image in question and break it down into
// "piece"x"piece"-pixel polygons and calculate the number of textures
// based from those calculations.
int mw = (surf_w & (piece - 1)) ? (surf_w / piece)+1 : (surf_w /piece);
int mh = (surf_h & (piece - 1)) ? (surf_h / piece)+1 : (surf_h /piece);
// for every square texture it takes four vertexes to express the two
// polygons.
Resize(mw * mh *2, mw * mh * 4);
// Set shader to additive if requested, else alpha or opaque depending on
// whether the texture has an alpha channel. Sorting is always set so that
// sortbias can be used to determine occlusion between the various 2D
// elements.
ShaderClass shader;
if (additive) {
shader = ShaderClass::_PresetAdditive2DShader;
} else {
if (ignore_alpha == false && Has_Alpha(sd.Format)) {
shader = ShaderClass::_PresetAlpha2DShader;
} else {
shader = ShaderClass::_PresetOpaque2DShader;
}
}
Enable_Sort();
// If we want to be able to colorize this bitmap later (by setting
// emissive color for the vertex material, or via a vertex emissive color
// array) we need to enable the primary gradient in the shader (it is
// disabled in the 2D presets), and set an appropriate vertex material.
if (colorizable) {
shader.Set_Primary_Gradient(ShaderClass::GRADIENT_MODULATE);
VertexMaterialClass *vertex_material = NEW_REF( VertexMaterialClass, ());
vertex_material->Set_Ambient(0.0f, 0.0f, 0.0f);
vertex_material->Set_Diffuse(0.0f, 0.0f, 0.0f);
vertex_material->Set_Specular(0.0f, 0.0f, 0.0f);
vertex_material->Set_Emissive(1.0f, 1.0f, 1.0f);
Set_Vertex_Material(vertex_material, true);
vertex_material->Release_Ref();
}
// Set desired shader.
Set_Shader(shader);
// loop through the rows and columns of the image and make valid
// textures from the pieces.
for (int lpy = 0, tlpy=0; lpy < mh; lpy++, tlpy += piece) {
for (int lpx = 0, tlpx = 0; lpx < mw; lpx++, tlpx += piece) {
// figure the desired width and height of the texture (max "piece")
int iw = MIN(piece, usable_width - (tlpx));
int ih = MIN(piece, usable_height - (tlpy));
int pot = MAX(Find_POT(iw), Find_POT(ih));
// create the texture and turn MIP-mapping off.
SurfaceClass *piece_surface=NEW_REF(SurfaceClass,(pot,pot,sd.Format));
piece_surface->Copy(0,0,tlpx,tlpy,pot,pot,surface);
TextureClass *piece_texture =NEW_REF(TextureClass,(piece_surface,MIP_LEVELS_1));
piece_texture->Get_Filter().Set_U_Addr_Mode(TextureFilterClass::TEXTURE_ADDRESS_CLAMP);
piece_texture->Get_Filter().Set_V_Addr_Mode(TextureFilterClass::TEXTURE_ADDRESS_CLAMP);
REF_PTR_RELEASE(piece_surface);
// calculate our actual texture coordinates based on the difference between
// the width and height of the texture and the width and height the font
// occupys.
float tw = (float)iw / (float)pot;
float th = (float)ih / (float)pot;
// convert image width and image height to normalized values
float vw = (float)iw / (float)resw;
float vh = (float)ih / (float)resh;
// figure out the screen space x and y positions of the object in question.
float x = screen_x + (((float)tlpx) / (float)resw);
float y = screen_y + (((float)tlpy) / (float)resh);
Set_Texture(piece_texture);
Begin_Tri_Strip();
Vertex( x, y, 0, 0, 0);
Vertex( x + vw, y, 0, tw, 0);
Vertex( x, y + vh, 0, 0, th);
Vertex( x + vw, y + vh, 0, tw, th);
End_Tri_Strip();
// release our reference to the texture
REF_PTR_RELEASE(piece_texture);
}
}
REF_PTR_RELEASE(tex);
REF_PTR_RELEASE(surface);
Set_Dirty();
}
Bitmap2DObjClass::Bitmap2DObjClass
(
TextureClass *texture,
float screen_x,
float screen_y,
bool center,
bool additive,
bool colorizable,
bool ignore_alpha
)
: DynamicScreenMeshClass(2, 4)
{
int resw, resh, resbits;
bool windowed;
// find the resolution (for centering and pixel to pixel translation)
WW3D::Get_Device_Resolution(resw, resh, resbits, windowed);
// This should be the correct way to do things
// but other code expects an aspect ratio of 1.0
// Hector Yee 2/22/01
//Set_Aspect(resh/(float)resw);
// Find the dimensions of the texture:
// SurfaceClass::SurfaceDescription sd;
// texture->Get_Level_Description(sd);
if (!texture->Is_Initialized())
TextureLoader::Request_Foreground_Loading(texture);
// convert image width and image height to normalized values
float vw = (float) texture->Get_Width() / (float)resw;
float vh = (float) texture->Get_Height() / (float)resh;
// if we requested the image to be centered around a point adjust the
// coordinates accordingly.
if (center) {
screen_x -= vw / 2;
screen_y -= vh / 2;
}
// Set shader to additive if requested, else alpha or opaque depending on whether the texture
// has an alpha channel. Sorting is never set - if you wish to sort these types of objects you
// should use static sort levels (note that static sort levels are not implemented for these
// objects yet, but it should be very simple to do).
ShaderClass shader;
if (additive) {
shader = ShaderClass::_PresetAdditive2DShader;
} else {
if (ignore_alpha == false && Has_Alpha(texture->Get_Texture_Format())) {
shader = ShaderClass::_PresetAlpha2DShader;
} else {
shader = ShaderClass::_PresetOpaque2DShader;
}
}
Disable_Sort();
// If we want to be able to colorize this bitmap later (by setting
// emissive color for the vertex material, or via a vertex emissive color
// array) we need to enable the primary gradient in the shader (it is
// disabled in the 2D presets), and set an appropriate vertex material.
if (colorizable) {
shader.Set_Primary_Gradient(ShaderClass::GRADIENT_MODULATE);
VertexMaterialClass *vertex_material = NEW_REF( VertexMaterialClass, ());
vertex_material->Set_Ambient(0.0f, 0.0f, 0.0f);
vertex_material->Set_Diffuse(0.0f, 0.0f, 0.0f);
vertex_material->Set_Specular(0.0f, 0.0f, 0.0f);
vertex_material->Set_Emissive(1.0f, 1.0f, 1.0f);
Set_Vertex_Material(vertex_material, true);
vertex_material->Release_Ref();
}
// Set desired shader.
Set_Shader(shader);
// Set texture to requested texture:
Set_Texture(texture);
Begin_Tri_Strip();
Vertex( screen_x, screen_y, 0, 0, 0);
Vertex( screen_x + vw, screen_y, 0, 1.0, 0);
Vertex( screen_x, screen_y + vh, 0, 0, 1.0);
Vertex( screen_x + vw, screen_y + vh, 0, 1.0, 1.0);
End_Tri_Strip();
Set_Dirty();
}
RenderObjClass * Bitmap2DObjClass::Clone(void) const
{
return NEW_REF( Bitmap2DObjClass, (*this));
}

View file

@ -0,0 +1,59 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***************************************************************************
* *
* Project Name : Commando/G *
* *
* $Archive:: /Commando/Code/ww3d2/bmp2d.h $*
* *
* $Author:: Hector_y $*
* *
* $Modtime:: 2/21/01 1:31p $*
* *
* $Revision:: 3 $*
* *
*-------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef BMP2D_H
#define BMP2D_H
#include "dynamesh.h"
class Bitmap2DObjClass : public DynamicScreenMeshClass
{
public:
Bitmap2DObjClass(const char *filename, float norm_x, float norm_y,
bool center, bool additive, bool colorizable = false, int width = -1, int height = -1, bool ignore_alpha = false);
Bitmap2DObjClass(TextureClass *texture, float norm_x, float norm_y,
bool center, bool additive, bool colorizable = false, bool ignore_alpha = false);
Bitmap2DObjClass( const Bitmap2DObjClass & src) : DynamicScreenMeshClass(src) {}
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const { return CLASSID_BITMAP2D; }
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,267 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/boxrobj.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 10/11/01 2:24p $*
* *
* $Revision:: 6 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef BOXROBJ_H
#define BOXROBJ_H
#include "always.h"
#include "rendobj.h"
#include "w3d_file.h"
#include "shader.h"
#include "proto.h"
#include "obbox.h"
class VertexMaterialClass;
/**
** BoxRenderObjClass: base class for AABox and OBBox collision boxes
**
** NOTE: these render objects were designed from the start to be used for
** collision boxes. They are designed to normally never render unless you
** set the display mask and then, all boxes of that type will render.
** The display mask is 'AND'ed with the collision bits in the base render
** object class to determine if the box should be rendered. WW3D provides
** an interface for setting this mask in your app.
**
** NOTE2: AABoxRenderObjClass is an axis-aligned box which will be positioned
** at a world-space offset (its local center) from the origin of the transform.
** This is done because AABoxes are used for rotationally invariant collision
** detection so we don't want the boxes to move around as the object rotates.
** For this reason, any AABoxes you use in a hierarchical model should be attached
** to the root and be constructed symmetrically...
**
** NOTE3: OBBoxRenderObjClass is an oriented box which is aligned with its transform
** but can have a center point that is offest from the transform's origin.
**
*/
class BoxRenderObjClass : public RenderObjClass
{
public:
BoxRenderObjClass(void);
BoxRenderObjClass(const W3dBoxStruct & def);
BoxRenderObjClass(const BoxRenderObjClass & src);
BoxRenderObjClass & operator = (const BoxRenderObjClass &);
virtual int Get_Num_Polys(void) const;
virtual const char * Get_Name(void) const;
virtual void Set_Name(const char * name);
void Set_Color(const Vector3 & color);
void Set_Opacity(float opacity) { Opacity = opacity; }
static void Init(void);
static void Shutdown(void);
static void Set_Box_Display_Mask(int mask);
static int Get_Box_Display_Mask(void);
void Set_Local_Center_Extent(const Vector3 & center,const Vector3 & extent);
void Set_Local_Min_Max(const Vector3 & min,const Vector3 & max);
const Vector3 & Get_Local_Center(void) { return ObjSpaceCenter; }
const Vector3 & Get_Local_Extent(void) { return ObjSpaceExtent; }
protected:
virtual void update_cached_box(void) = 0;
void render_box(RenderInfoClass & rinfo,const Vector3 & center,const Vector3 & extent);
void vis_render_box(SpecialRenderInfoClass & rinfo,const Vector3 & center,const Vector3 & extent);
char Name[2*W3D_NAME_LEN];
Vector3 Color;
Vector3 ObjSpaceCenter;
Vector3 ObjSpaceExtent;
float Opacity;
static bool IsInitted;
static int DisplayMask;
};
inline void BoxRenderObjClass::Set_Local_Center_Extent(const Vector3 & center,const Vector3 & extent)
{
ObjSpaceCenter = center;
ObjSpaceExtent = extent;
update_cached_box();
}
inline void BoxRenderObjClass::Set_Local_Min_Max(const Vector3 & min,const Vector3 & max)
{
ObjSpaceCenter = (max + min) / 2.0f;
ObjSpaceExtent = (max - min) / 2.0f;
update_cached_box();
}
/*
** AABoxRenderObjClass -- RenderObject for axis-aligned collision boxes.
*/
class AABoxRenderObjClass : public W3DMPO, public BoxRenderObjClass
{
W3DMPO_GLUE(AABoxRenderObjClass)
public:
AABoxRenderObjClass(void);
AABoxRenderObjClass(const W3dBoxStruct & def);
AABoxRenderObjClass(const AABoxRenderObjClass & src);
AABoxRenderObjClass(const AABoxClass & box);
AABoxRenderObjClass & operator = (const AABoxRenderObjClass &);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface
/////////////////////////////////////////////////////////////////////////////
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const;
virtual void Render(RenderInfoClass & rinfo);
virtual void Special_Render(SpecialRenderInfoClass & rinfo);
virtual void Set_Transform(const Matrix3D &m);
virtual void Set_Position(const Vector3 &v);
virtual bool Cast_Ray(RayCollisionTestClass & raytest);
virtual bool Cast_AABox(AABoxCollisionTestClass & boxtest);
virtual bool Cast_OBBox(OBBoxCollisionTestClass & boxtest);
virtual bool Intersect_AABox(AABoxIntersectionTestClass & boxtest);
virtual bool Intersect_OBBox(OBBoxIntersectionTestClass & boxtest);
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const;
virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const;
/////////////////////////////////////////////////////////////////////////////
// AABoxRenderObjClass Interface
/////////////////////////////////////////////////////////////////////////////
const AABoxClass & Get_Box(void);
protected:
virtual void update_cached_box(void);
AABoxClass CachedBox;
};
inline const AABoxClass & AABoxRenderObjClass::Get_Box(void)
{
Validate_Transform();
update_cached_box();
return CachedBox;
}
/*
** OBBoxRenderObjClass - render object for oriented collision boxes
*/
class OBBoxRenderObjClass : public W3DMPO, public BoxRenderObjClass
{
W3DMPO_GLUE(OBBoxRenderObjClass)
public:
OBBoxRenderObjClass(void);
OBBoxRenderObjClass(const W3dBoxStruct & def);
OBBoxRenderObjClass(const OBBoxRenderObjClass & src);
OBBoxRenderObjClass(const OBBoxClass & box);
OBBoxRenderObjClass & operator = (const OBBoxRenderObjClass &);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface
/////////////////////////////////////////////////////////////////////////////
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const;
virtual void Render(RenderInfoClass & rinfo);
virtual void Special_Render(SpecialRenderInfoClass & rinfo);
virtual void Set_Transform(const Matrix3D &m);
virtual void Set_Position(const Vector3 &v);
virtual bool Cast_Ray(RayCollisionTestClass & raytest);
virtual bool Cast_AABox(AABoxCollisionTestClass & boxtest);
virtual bool Cast_OBBox(OBBoxCollisionTestClass & boxtest);
virtual bool Intersect_AABox(AABoxIntersectionTestClass & boxtest);
virtual bool Intersect_OBBox(OBBoxIntersectionTestClass & boxtest);
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const;
virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const;
/////////////////////////////////////////////////////////////////////////////
// OBBoxRenderObjClass Interface
/////////////////////////////////////////////////////////////////////////////
OBBoxClass & Get_Box(void);
protected:
virtual void update_cached_box(void);
OBBoxClass CachedBox;
};
/*
** Loader for boxes
*/
class BoxLoaderClass : public PrototypeLoaderClass
{
public:
virtual int Chunk_Type (void) { return W3D_CHUNK_BOX; }
virtual PrototypeClass * Load_W3D(ChunkLoadClass & cload);
};
// ----------------------------------------------------------------------------
/*
** Prototype for Box objects
*/
class BoxPrototypeClass : public W3DMPO, public PrototypeClass
{
W3DMPO_GLUE(BoxPrototypeClass)
public:
BoxPrototypeClass(W3dBoxStruct box);
virtual const char * Get_Name(void) const;
virtual int Get_Class_ID(void) const;
virtual RenderObjClass * Create(void);
virtual void DeleteSelf() { delete this; }
private:
W3dBoxStruct Definition;
};
/*
** Instance of the loader which the asset manager installs
*/
extern BoxLoaderClass _BoxLoader;
#endif

View file

@ -0,0 +1,241 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/BW_Render.cpp $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 2/06/01 10:57a $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "bw_render.h"
#include "vp.h"
#include <string.h>
BW_Render::Buffer::Buffer(unsigned char* buffer_, int scale_)
:
buffer(buffer_),
scale(scale_),
minv(3),
maxv(scale_-3)
{
}
BW_Render::Buffer::~Buffer()
{
}
void BW_Render::Buffer::Set_H_Line(int start_x, int end_x, int y)
{
if (y<minv || y>=maxv || end_x<minv || start_x>=maxv) return;
if (start_x<minv) start_x=minv;
if (end_x>=maxv) end_x=maxv-1;
unsigned char* ptr=buffer+scale*y+start_x;
int w=end_x-start_x;
if (w) {
::memset(ptr,0x00,w);
/* // Blurring (test)
*(ptr-1)&=0x80;
*(ptr-2)&=0xc0;
*(ptr+w)&=0x80;
*(ptr+w+1)&=0xc0;
for (int a=0;a<w;++a) {
*(ptr-scale+a)&=0xc0;
*(ptr+scale+a)&=0xc0;
}
*/
}
}
void BW_Render::Buffer::Fill(unsigned char c)
{
memset(buffer,c,scale*scale);
}
// ------------------------------------------------------------------------------
BW_Render::BW_Render(unsigned char* buffer, int buffer_scale)
:
pixel_buffer(buffer,buffer_scale)
{
}
BW_Render::~BW_Render()
{
}
void BW_Render::Fill(unsigned char c)
{
pixel_buffer.Fill(c);
}
// Sets the vertex coordinate buffer location and scales the vertex locations to the kjkj
void BW_Render::Set_Vertex_Locations(Vector2* vertices_,int count)
{
vertices=vertices_;
float half_scale=pixel_buffer.Scale()*0.5f;
VectorProcessorClass::MulAdd(
reinterpret_cast<float*>(vertices),
half_scale,
half_scale,
count*2);
}
// --------------------------------------------------------------------
static inline bool Cull(
const Vector2& c1,
const Vector2& c2,
const Vector2& c3)
{
float x1=c2[0]-c1[0];
float y1=c2[1]-c1[1];
float x2=c3[0]-c1[0];
float y2=c3[1]-c1[1];
float r=x1*y2-x2*y1;
if (r<0) return false;
return true;
}
void BW_Render::Render_Triangle_Strip(const unsigned long* indices,int index_count)
{
index_count-=2;
bool b=false;
for (int n=0;n<index_count;++n) {
b=!b;
int idx_1=indices[0];
int idx_2=indices[1];
int idx_3=indices[2];
indices++;
if (Cull(vertices[idx_1],vertices[idx_2],vertices[idx_3])==b) continue;
Vector2 corner_1(vertices[idx_1][0],vertices[idx_1][1]);
Vector2 corner_2(vertices[idx_2][0],vertices[idx_2][1]);
Vector2 corner_3(vertices[idx_3][0],vertices[idx_3][1]);
// Sort the corners on y axis
if (corner_2[1]<corner_1[1]) Swap(corner_1,corner_2);
if (corner_3[1]<corner_1[1]) Swap(corner_1,corner_3);
if (corner_3[1]<corner_2[1]) Swap(corner_2,corner_3);
Vector3i yci(WWMath::Float_To_Long(corner_1[1]),WWMath::Float_To_Long(corner_2[1]),WWMath::Float_To_Long(corner_3[1]));
Vector3 xcf(corner_1[0],corner_2[0],corner_3[0]);
Render_Preprocessed_Triangle(xcf,yci);
}
}
void BW_Render::Render_Triangles(const unsigned long* indices,int index_count)
{
index_count/=3;
for (int n=0;n<index_count;++n) {
int idx_1=*indices++;
int idx_2=*indices++;
int idx_3=*indices++;
if (Cull(vertices[idx_1],vertices[idx_2],vertices[idx_3])) continue;
Vector2 corner_1(vertices[idx_1][0],vertices[idx_1][1]);
Vector2 corner_2(vertices[idx_2][0],vertices[idx_2][1]);
Vector2 corner_3(vertices[idx_3][0],vertices[idx_3][1]);
// Sort the corners on y axis
if (corner_2[1]<corner_1[1]) Swap(corner_1,corner_2);
if (corner_3[1]<corner_1[1]) Swap(corner_1,corner_3);
if (corner_3[1]<corner_2[1]) Swap(corner_2,corner_3);
Vector3i yci(WWMath::Float_To_Long(corner_1[1]),WWMath::Float_To_Long(corner_2[1]),WWMath::Float_To_Long(corner_3[1]));
Vector3 xcf(corner_1[0],corner_2[0],corner_3[0]);
Render_Preprocessed_Triangle(xcf,yci);
}
}
void BW_Render::Render_Preprocessed_Triangle(Vector3& xcf,Vector3i& yci)
{
float x_left=xcf[0];
float x_right=x_left;
int ycnt=yci[1]-yci[0];
int y=yci[0];
if (ycnt) {
float x_step_1=(xcf[1]-xcf[0])/float(ycnt);
float x_step_2=(xcf[2]-xcf[0])/float(yci[2]-y);
if (x_step_1>x_step_2) {
float t=x_step_1;
x_step_1=x_step_2;
x_step_2=t;
}
while (ycnt>0) {
pixel_buffer.Set_H_Line(WWMath::Float_To_Long(x_left),WWMath::Float_To_Long(x_right),y);
x_left+=x_step_1;
x_right+=x_step_2;
ycnt--;
y++;
}
}
else {
if (xcf[0]<xcf[1]) {
x_left=xcf[0];
x_right=xcf[1];
}
else {
x_right=xcf[0];
x_left=xcf[1];
}
}
ycnt=yci[2]-yci[1];
y=yci[1];
if (ycnt) {
float one_per_ycnt=1.0f/float(ycnt);
float x_step_1=(xcf[2]-x_left)*one_per_ycnt;
float x_step_2=(xcf[2]-x_right)*one_per_ycnt;
while (ycnt>0) {
pixel_buffer.Set_H_Line(WWMath::Float_To_Long(x_left),WWMath::Float_To_Long(x_right),y);
x_left+=x_step_1;
x_right+=x_step_2;
ycnt--;
y++;
}
}
}

View file

@ -0,0 +1,85 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d *
* *
* $Archive:: /Commando/Code/ww3d2/bw_render.h $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 3/26/01 4:53p $*
* *
* $Revision:: 3 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef BW_RENDER_H__
#define BW_RENDER_H__
#include "always.h"
#include "vector2.h"
#include "vector3.h"
#include "vector3i.h"
class BW_Render
{
// Internal pixel buffer used by the triangle renderer
// The buffer is not allocated or freed by this class.
class Buffer
{
unsigned char* buffer;
int scale;
int minv;
int maxv;
public:
Buffer(unsigned char* buffer, int scale);
~Buffer();
void Set_H_Line(int start_x, int end_x, int y);
void Fill(unsigned char c);
inline int Scale() const { return scale; }
} pixel_buffer;
Vector2* vertices;
void Render_Preprocessed_Triangle(Vector3& xcf,Vector3i& yci);
public:
BW_Render(unsigned char* buffer, int scale);
~BW_Render();
void Fill(unsigned char c);
void Set_Vertex_Locations(Vector2* vertices,int count); // Warning! Contents are modified!
void Render_Triangles(const unsigned long* indices,int index_count);
void Render_Triangle_Strip(const unsigned long* indices,int index_count);
};
#endif // BW_RENDER_H__

View file

@ -0,0 +1,242 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d2 *
* *
* $Archive:: /Commando/Code/ww3d2/bwrender.cpp $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 4/04/01 10:14a $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "bwrender.h"
#include "vp.h"
#include <string.h>
BWRenderClass::Buffer::Buffer(unsigned char* buffer_, int scale_)
:
buffer(buffer_),
scale(scale_),
minv(3),
maxv(scale_-3)
{
}
BWRenderClass::Buffer::~Buffer()
{
}
void BWRenderClass::Buffer::Set_H_Line(int start_x, int end_x, int y)
{
if (y<minv || y>=maxv || end_x<minv || start_x>=maxv) return;
if (start_x<minv) start_x=minv;
if (end_x>=maxv) end_x=maxv-1;
unsigned char* ptr=buffer+scale*y+start_x;
int w=end_x-start_x;
if (w) {
::memset(ptr,0x00,w);
/* // Blurring (test)
*(ptr-1)&=0x80;
*(ptr-2)&=0xc0;
*(ptr+w)&=0x80;
*(ptr+w+1)&=0xc0;
for (int a=0;a<w;++a) {
*(ptr-scale+a)&=0xc0;
*(ptr+scale+a)&=0xc0;
}
*/
}
}
void BWRenderClass::Buffer::Fill(unsigned char c)
{
memset(buffer,c,scale*scale);
}
// ------------------------------------------------------------------------------
BWRenderClass::BWRenderClass(unsigned char* buffer, int buffer_scale)
:
pixel_buffer(buffer,buffer_scale)
{
}
BWRenderClass::~BWRenderClass()
{
}
void BWRenderClass::Fill(unsigned char c)
{
pixel_buffer.Fill(c);
}
// Sets the vertex coordinate buffer location and scales the vertex locations to the kjkj
void BWRenderClass::Set_Vertex_Locations(Vector2* vertices_,int count)
{
vertices=vertices_;
float half_scale=pixel_buffer.Scale()*0.5f;
VectorProcessorClass::MulAdd(
reinterpret_cast<float*>(vertices),
half_scale,
half_scale,
count*2);
}
// --------------------------------------------------------------------
static inline bool Cull(
const Vector2& c1,
const Vector2& c2,
const Vector2& c3)
{
float x1=c2[0]-c1[0];
float y1=c2[1]-c1[1];
float x2=c3[0]-c1[0];
float y2=c3[1]-c1[1];
float r=x1*y2-x2*y1;
if (r<0) return false;
return true;
}
void BWRenderClass::Render_Triangle_Strip(const unsigned long* indices,int index_count)
{
index_count-=2;
bool b=false;
for (int n=0;n<index_count;++n) {
b=!b;
int idx_1=indices[0];
int idx_2=indices[1];
int idx_3=indices[2];
indices++;
if (Cull(vertices[idx_1],vertices[idx_2],vertices[idx_3])==b) continue;
Vector2 corner_1(vertices[idx_1][0],vertices[idx_1][1]);
Vector2 corner_2(vertices[idx_2][0],vertices[idx_2][1]);
Vector2 corner_3(vertices[idx_3][0],vertices[idx_3][1]);
// Sort the corners on y axis
if (corner_2[1]<corner_1[1]) Swap(corner_1,corner_2);
if (corner_3[1]<corner_1[1]) Swap(corner_1,corner_3);
if (corner_3[1]<corner_2[1]) Swap(corner_2,corner_3);
Vector3i yci(WWMath::Float_To_Long(corner_1[1]),WWMath::Float_To_Long(corner_2[1]),WWMath::Float_To_Long(corner_3[1]));
Vector3 xcf(corner_1[0],corner_2[0],corner_3[0]);
Render_Preprocessed_Triangle(xcf,yci);
}
}
void BWRenderClass::Render_Triangles(const unsigned long* indices,int index_count)
{
index_count/=3;
for (int n=0;n<index_count;++n) {
int idx_1=*indices++;
int idx_2=*indices++;
int idx_3=*indices++;
if (Cull(vertices[idx_1],vertices[idx_2],vertices[idx_3])) continue;
Vector2 corner_1(vertices[idx_1][0],vertices[idx_1][1]);
Vector2 corner_2(vertices[idx_2][0],vertices[idx_2][1]);
Vector2 corner_3(vertices[idx_3][0],vertices[idx_3][1]);
// Sort the corners on y axis
if (corner_2[1]<corner_1[1]) Swap(corner_1,corner_2);
if (corner_3[1]<corner_1[1]) Swap(corner_1,corner_3);
if (corner_3[1]<corner_2[1]) Swap(corner_2,corner_3);
Vector3i yci(WWMath::Float_To_Long(corner_1[1]),WWMath::Float_To_Long(corner_2[1]),WWMath::Float_To_Long(corner_3[1]));
Vector3 xcf(corner_1[0],corner_2[0],corner_3[0]);
Render_Preprocessed_Triangle(xcf,yci);
}
}
void BWRenderClass::Render_Preprocessed_Triangle(Vector3& xcf,Vector3i& yci)
{
float x_left=xcf[0];
float x_right=x_left;
int ycnt=yci[1]-yci[0];
int y=yci[0];
if (ycnt) {
float x_step_1=(xcf[1]-xcf[0])/float(ycnt);
float x_step_2=(xcf[2]-xcf[0])/float(yci[2]-y);
if (x_step_1>x_step_2) {
float t=x_step_1;
x_step_1=x_step_2;
x_step_2=t;
}
while (ycnt>0) {
pixel_buffer.Set_H_Line(WWMath::Float_To_Long(x_left),WWMath::Float_To_Long(x_right),y);
x_left+=x_step_1;
x_right+=x_step_2;
ycnt--;
y++;
}
}
else {
if (xcf[0]<xcf[1]) {
x_left=xcf[0];
x_right=xcf[1];
}
else {
x_right=xcf[0];
x_left=xcf[1];
}
}
ycnt=yci[2]-yci[1];
y=yci[1];
if (ycnt) {
float one_per_ycnt=1.0f/float(ycnt);
float x_step_1=(xcf[2]-x_left)*one_per_ycnt;
float x_step_2=(xcf[2]-x_right)*one_per_ycnt;
while (ycnt>0) {
pixel_buffer.Set_H_Line(WWMath::Float_To_Long(x_left),WWMath::Float_To_Long(x_right),y);
x_left+=x_step_1;
x_right+=x_step_2;
ycnt--;
y++;
}
}
}

View file

@ -0,0 +1,96 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : ww3d2 *
* *
* $Archive:: /Commando/Code/ww3d2/bwrender.h $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 4/04/01 10:36a $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef BWRENDER_H
#define BWRENDER_H
#include "always.h"
#include "vector2.h"
#include "vector3.h"
#include "vector3i.h"
/**
** BWRenderClass
** This class implements a simple black-and-white triangle rasterizer which
** can be used to generate shadow textures. It is faster than a general purpose
** software renderer due to the fact that no z-buffering or sorting is needed and
** texturing isn't supported.
** (gth) 04/02/2001 - I'm going to add render-to-texture code to Renegade so this
** class may be obsolete.
*/
class BWRenderClass
{
// Internal pixel buffer used by the triangle renderer
// The buffer is not allocated or freed by this class.
class Buffer
{
unsigned char* buffer;
int scale;
int minv;
int maxv;
public:
Buffer(unsigned char* buffer, int scale);
~Buffer();
void Set_H_Line(int start_x, int end_x, int y);
void Fill(unsigned char c);
inline int Scale() const { return scale; }
} pixel_buffer;
Vector2* vertices;
void Render_Preprocessed_Triangle(Vector3& xcf,Vector3i& yci);
public:
BWRenderClass(unsigned char* buffer, int scale);
~BWRenderClass();
void Fill(unsigned char c);
void Set_Vertex_Locations(Vector2* vertices,int count); // Warning! Contents are modified!
void Render_Triangles(const unsigned long* indices,int index_count);
void Render_Triangle_Strip(const unsigned long* indices,int index_count);
};
#endif //BWRENDER_H

View file

@ -0,0 +1,830 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/camera.cpp $*
* *
* Org Author:: Greg_h *
* *
* $Author:: Kenny Mitchell *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 24 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CameraClass::CameraClass -- constructor *
* CameraClass::CameraClass -- copy constructor *
* CameraClass::operator == -- assignment operator *
* CameraClass::~CameraClass -- destructor *
* CameraClass::Clone -- virtual copy constructor *
* CameraClass::Add -- add the camera to the world? *
* CameraClass::Remove -- Remove the camera from the world? *
* CameraClass::Get_Obj_Space_Bounding_Sphere -- returns the object space bounding sphere *
* CameraClass::Get_Object_Space_Bounding_Box -- returns the object space bounding box *
* CameraClass::Set_Transform -- set the transform of the camera *
* CameraClass::Set_Position -- Set the position of the camera *
* CameraClass::Set_Animation -- set the animation state of the camera *
* CameraClass::Set_Animation -- Set the animation state of the camera *
* CameraClass::Set_Animation -- set the animation state of the camera *
* CameraClass::Set_Animation -- set the animation state of the camera *
* CameraClass::Set_View_Plane -- control over the view plane *
* CameraClass::Set_View_Plane -- set the viewplane using fov angles *
* CameraClass::Set_Focal_Length -- set the view plane using focal length and aspect ratio *
* CameraClass::Get_View_Plane -- get the corners of the current view plane *
* CameraClass::Project -- project a point from ws to the view plane *
* CameraClass::Project_Camera_Space_Point -- Project a point that is already in camera spac *
* CameraClass::Un_Project -- "unproject" a point from the view plane to world space *
* CameraClass::Transform_To_View_Space -- transforms the given world space point to camera *
* CameraClass::Rotate_To_View_Space -- rotates the given world space vector to camera space *
* CameraClass::Get_Near_Clip_Bounding_Box -- returns an obb that contains near clip plane *
* CameraCLass::Update_Frustum -- updates the frustum parameters *
* CameraClass::Cull_Box -- tests whether the given box can be culled *
* CameraClass::Device_To_View_Space -- converts the given device coordinate to view space *
* CameraClass::Device_To_World_Space -- converts given device coord to world space *
* CameraClass::Camera_Push -- pushes the camera's parameters into the given GERD *
* CameraClass::Camera_Pop -- pops the camera's parameters from the given GERD *
* CameraClass::Set_Aspect_Ratio -- sets the aspect ratio of the camera *
* CameraClass::Apply_D3D_State -- sets the D3D states controlled by the camera *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "camera.h"
#include "ww3d.h"
#include "matrix4.h"
#include "dx8wrapper.h"
/***********************************************************************************************
* CameraClass::CameraClass -- constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
CameraClass::CameraClass(void) :
Projection(PERSPECTIVE),
Viewport(Vector2(0,0),Vector2(1,1)), // pixel viewport to render into
AspectRatio(4.0f/3.0f),
ZNear(1.0f), // near clip plane distance
ZFar(1000.0f), // far clip plane distance
ZBufferMin(0.0f), // smallest value we'll write into the z-buffer
ZBufferMax(1.0f), // largest value we'll write into the z-buffer
FrustumValid(false)
{
Set_Transform(Matrix3D(1));
Set_View_Plane(DEG_TO_RADF(50.0f));
}
/***********************************************************************************************
* CameraClass::CameraClass -- copy constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
* 4/13/2001 hy : added in copy code for new member functions *
*=============================================================================================*/
CameraClass::CameraClass(const CameraClass & src) :
RenderObjClass(src),
Projection(src.Projection),
Viewport(src.Viewport),
ViewPlane(src.ViewPlane),
ZNear(src.ZNear),
ZFar(src.ZFar),
FrustumValid(src.FrustumValid),
Frustum(src.Frustum),
NearClipBBox(src.NearClipBBox),
ProjectionTransform(src.ProjectionTransform),
CameraInvTransform(src.CameraInvTransform),
AspectRatio(src.AspectRatio),
ZBufferMin(src.ZBufferMin),
ZBufferMax(src.ZBufferMax)
{
// just being paraniod in case any parent class doesn't completely copy the entire state...
FrustumValid = false;
}
/***********************************************************************************************
* CameraClass::operator == -- assignment operator *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
CameraClass & CameraClass::operator = (const CameraClass & that)
{
if (this != &that) {
RenderObjClass::operator = (that);
Projection = that.Projection;
Viewport = that.Viewport;
ViewPlane = that.ViewPlane;
ZNear = that.ZNear;
ZFar = that.ZFar;
FrustumValid = that.FrustumValid;
Frustum = that.Frustum;
NearClipBBox = that.NearClipBBox;
ProjectionTransform = that.ProjectionTransform;
CameraInvTransform = that.CameraInvTransform;
// just being paraniod in case any parent class doesn't completely copy the entire state...
FrustumValid = false;
}
return * this;
}
/***********************************************************************************************
* CameraClass::~CameraClass -- destructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
CameraClass::~CameraClass(void)
{
}
/***********************************************************************************************
* CameraClass::Clone -- virtual copy constructor *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
RenderObjClass * CameraClass::Clone(void) const
{
return NEW_REF( CameraClass, (*this) );
}
/***********************************************************************************************
* CameraClass::Get_Obj_Space_Bounding_Sphere -- returns the object space bounding sphere *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/29/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const
{
sphere.Center.Set(0,0,0);
sphere.Radius = ZFar; // could optimize this but its not really used.
}
/***********************************************************************************************
* CameraClass::Get_Object_Space_Bounding_Box -- returns the object space bounding box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/29/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Get_Obj_Space_Bounding_Box(AABoxClass & box) const
{
box.Center.Set(0,0,0);
box.Extent.Set(ZFar,ZFar,ZFar); // could optimize this but its not really used.
}
/***********************************************************************************************
* CameraClass::Set_Transform -- set the transform of the camera *
* *
* This is over-ridden to invalidate the cached frustum parameters *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/29/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Set_Transform(const Matrix3D &m)
{
RenderObjClass::Set_Transform(m);
FrustumValid = false;
}
/***********************************************************************************************
* CameraClass::Set_Position -- Set the position of the camera *
* *
* This is overriden to invalidate the cached frustum parameters *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/29/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Set_Position(const Vector3 &v)
{
RenderObjClass::Set_Position(v);
FrustumValid = false;
}
/***********************************************************************************************
* CameraClass::Set_View_Plane -- control over the view plane *
* *
* INPUT: *
* min - x,y of the upper left corner of the view rectangle (dist is assumed to be 1.0) *
* max - x,y of the lower right corner of the view rectangle *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Set_View_Plane(const Vector2 & vmin,const Vector2 & vmax)
{
ViewPlane.Min = vmin;
ViewPlane.Max = vmax;
AspectRatio = (vmax.X - vmin.X) / (vmax.Y - vmin.Y);
FrustumValid = false;
}
/***********************************************************************************************
* CameraClass::Set_View_Plane -- set the viewplane using fov angles *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Set_View_Plane(float hfov,float vfov)
{
float width_half = tan(hfov/2.0);
float height_half = 0.0f;
if (vfov == -1) {
height_half = (1.0f / AspectRatio) * width_half; // use the aspect ratio
} else {
height_half = tan(vfov/2.0);
AspectRatio = width_half / height_half; // or, initialize the aspect ratio
}
ViewPlane.Min.Set(-width_half,-height_half);
ViewPlane.Max.Set(width_half,height_half);
FrustumValid = false;
}
/***********************************************************************************************
* CameraClass::Set_Aspect_Ratio -- sets the aspect ratio of the camera *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/29/2001 gth : Created. *
*=============================================================================================*/
void CameraClass::Set_Aspect_Ratio(float width_to_height)
{
AspectRatio = width_to_height;
ViewPlane.Min.Y = ViewPlane.Min.X / AspectRatio;
ViewPlane.Max.Y = ViewPlane.Max.X / AspectRatio;
FrustumValid = false;
}
/***********************************************************************************************
* CameraClass::Get_View_Plane -- get the corners of the current view plane *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Get_View_Plane(Vector2 & set_min,Vector2 & set_max) const
{
set_min = ViewPlane.Min;
set_max = ViewPlane.Max;
}
/***********************************************************************************************
* CameraClass::Project -- project a point from ws to the view plane *
* *
* INPUT: *
* dest - will be set to a point on the normalized view plane. x,y, and z range from -1 to 1 *
* ws_point - input point in world space *
* *
* OUTPUT: *
* ProjectionResType indicating whether the point was in the frustum *
* *
* WARNINGS: *
* When the input is behind the near clip plane, 0,0,0 is returned. *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
CameraClass::ProjectionResType CameraClass::Project(Vector3 & dest,const Vector3 & ws_point) const
{
Update_Frustum();
Vector3 cam_point;
Matrix3D::Transform_Vector(CameraInvTransform,ws_point,&cam_point);
if (cam_point.Z > -ZNear) {
dest.Set(0,0,0);
return OUTSIDE_NEAR_CLIP;
}
Vector4 view_point = ProjectionTransform * cam_point;
float oow = 1.0f / view_point.W;
dest.X = view_point.X * oow;
dest.Y = view_point.Y * oow;
dest.Z = view_point.Z * oow;
if (dest.Z > 1.0f) {
return OUTSIDE_FAR_CLIP;
}
if ((dest.X < -1.0f) || (dest.X > 1.0f) || (dest.Y < -1.0f) || (dest.Y > 1.0f)) {
return OUTSIDE_FRUSTUM;
}
return INSIDE_FRUSTUM;
}
/***********************************************************************************************
* CameraClass::Project_Camera_Space_Point -- Project a point that is already in camera space *
* *
* INPUT: *
* dest - will be set to a point on the normalized view plane. x,y, and z range from -1 to 1 *
* cam_point - input point in camera space *
* *
* OUTPUT: *
* ProjectionResType indicating whether the point was in the frustum *
* *
* WARNINGS: *
* When the input is behind the near clip plane, 0,0,0 is returned. *
* *
* HISTORY: *
* 11/17/2000 gth : Created. *
*=============================================================================================*/
CameraClass::ProjectionResType
CameraClass::Project_Camera_Space_Point(Vector3 & dest,const Vector3 & cam_point) const
{
Update_Frustum();
if ( cam_point.Z > -ZNear + WWMATH_EPSILON) {
dest.Set(0,0,0);
return OUTSIDE_NEAR_CLIP;
}
Vector4 view_point = ProjectionTransform * cam_point;
float oow = 1.0f / view_point.W;
dest.X = view_point.X * oow;
dest.Y = view_point.Y * oow;
dest.Z = view_point.Z * oow;
if (dest.Z > 1.0f) {
return OUTSIDE_FAR_CLIP;
}
if ((dest.X < -1.0f) || (dest.X > 1.0f) || (dest.Y < -1.0f) || (dest.Y > 1.0f)) {
return OUTSIDE_FRUSTUM;
}
return INSIDE_FRUSTUM;
}
/***********************************************************************************************
* CameraClass::Un_Project -- "unproject" a point from the view plane to world space *
* *
* The given point is assumed to be on the view_plane at z=-1. The 3D world space point that *
* this represents will be returned in dest. *
* *
* INPUT: *
* dest - will be filled in with the 3D world space point that the given point represents *
* view_point - point on the view_plane to be un-projected *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/21/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Un_Project(Vector3 & dest,const Vector2 & view_point) const
{
/*
** map view_point.X from -1..1 to ViewPlaneMin.X..ViewPlaneMax.X
** map view_point.Y from -1..1 to ViewPlaneMin.X..ViewPlaneMax.X
*/
float vpdx = ViewPlane.Max.X - ViewPlane.Min.X;
float vpdy = ViewPlane.Max.Y - ViewPlane.Min.Y;
Vector3 point;
point.X = ViewPlane.Min.X + vpdx * (view_point.X + 1.0f) * 0.5f;
point.Y = ViewPlane.Min.Y + vpdy * (view_point.Y + 1.0f) * 0.5f;
point.Z = -1.0f;
Matrix3D::Transform_Vector(Transform,point,&dest);
}
/***********************************************************************************************
* CameraClass::Transform_To_View_Space -- transforms the given world space point to camera sp *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/22/2001 gth : Created. *
*=============================================================================================*/
void CameraClass::Transform_To_View_Space(Vector3 & dest,const Vector3 & ws_point) const
{
Update_Frustum();
Matrix3D::Transform_Vector(CameraInvTransform,ws_point,&dest);
}
/***********************************************************************************************
* CameraClass::Rotate_To_View_Space -- rotates the given world space vector to camera space *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 2/22/2001 gth : Created. *
*=============================================================================================*/
void CameraClass::Rotate_To_View_Space(Vector3 & dest,const Vector3 & ws_vector) const
{
Update_Frustum();
Matrix3D::Rotate_Vector(CameraInvTransform,ws_vector,&dest);
}
/***********************************************************************************************
* CameraClass::Get_Near_Clip_Bounding_Box -- returns an obb that contains near clip plane *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 8/25/99 GTH : Created. *
*=============================================================================================*/
const OBBoxClass & CameraClass::Get_Near_Clip_Bounding_Box(void) const
{
Update_Frustum();
return NearClipBBox;
}
/***********************************************************************************************
* CameraClass::Cull_Box -- tests whether the given box can be culled *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/8/98 GTH : Created. *
*=============================================================================================*/
bool CameraClass::Cull_Box(const AABoxClass & box) const
{
const FrustumClass & frustum = Get_Frustum();
return CollisionMath::Overlap_Test(frustum,box) == CollisionMath::OUTSIDE;
}
/***********************************************************************************************
* CameraClass::Update_Frustum -- updates the frustum parameters *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/29/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Update_Frustum(void) const
{
if (FrustumValid) return;
Vector2 vpmin,vpmax;
float znear,zfar;
float znear_dist,zfar_dist;
Matrix3D cam_mat = Get_Transform();
Get_View_Plane(vpmin, vpmax); // Normalized view plane at a depth of 1.0
Get_Clip_Planes(znear_dist, zfar_dist);
// Forward is negative Z in our viewspace coordinate system.
znear = -znear_dist;
zfar = -zfar_dist;
// Update the frustum
FrustumValid = true;
Frustum.Init(cam_mat,vpmin,vpmax,znear,zfar);
ViewSpaceFrustum.Init(Matrix3D(1),vpmin,vpmax,znear,zfar);
// Update the OBB around the near clip rectangle
#ifdef ALLOW_TEMPORARIES
NearClipBBox.Center = cam_mat * Vector3(0,0,znear);
#else
cam_mat.mulVector3(Vector3(0,0,znear), NearClipBBox.Center);
#endif
NearClipBBox.Extent.X = (vpmax.X - vpmin.X) * (-znear) * 0.5f; // (near_clip_x / |znear|) == (vpmin.X / 1.0f)...
NearClipBBox.Extent.Y = (vpmax.Y - vpmin.Y) * (-znear) * 0.5f;
NearClipBBox.Extent.Z = 0.01f;
NearClipBBox.Basis.Set(cam_mat);
// Update the inverse camera matrix
Transform.Get_Inverse(CameraInvTransform);
// Update the projection matrix
if (Projection == PERSPECTIVE) {
ProjectionTransform.Init_Perspective( vpmin.X*znear_dist, vpmax.X*znear_dist,
vpmin.Y*znear_dist, vpmax.Y*znear_dist,
znear_dist, zfar_dist );
} else {
ProjectionTransform.Init_Ortho( vpmin.X,vpmax.X,vpmin.Y,vpmax.Y,znear_dist,zfar_dist);
}
}
/***********************************************************************************************
* CameraClass::Device_To_View_Space -- converts the given device coordinate to view space *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/8/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Device_To_View_Space(const Vector2 & device_coord,Vector3 * set_view)
{
int res_width;
int res_height;
int res_bits;
bool windowed;
WW3D::Get_Render_Target_Resolution(res_width,res_height,res_bits,windowed);
// convert the device coordinates into normalized device coordinates:
Vector2 ndev;
ndev.X = device_coord.X / (float)res_width;
ndev.Y = device_coord.Y / (float)res_height;
// view space rectangle which corresponds to the viewport
Vector2 vs_min;
Vector2 vs_max;
Get_View_Plane(vs_min,vs_max);
// mapping from the viewport coordinates to view space coordinates
set_view->X = vs_min.X + (ndev.X - Viewport.Min.X) * (vs_max.X - vs_min.X) / (Viewport.Width());
set_view->Y = vs_max.Y - (ndev.Y - Viewport.Min.Y) * (vs_max.Y - vs_min.Y) / (Viewport.Height());
set_view->Z = -1.0f;
}
/***********************************************************************************************
* CameraClass::Device_To_World_Space -- converts given device coord to world space *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 12/8/98 GTH : Created. *
*=============================================================================================*/
void CameraClass::Device_To_World_Space(const Vector2 & device_coord,Vector3 * world_coord)
{
Vector3 vs;
Device_To_View_Space(device_coord,&vs);
Matrix3D::Transform_Vector(Transform,vs,world_coord);
}
/***********************************************************************************************
* CameraClass::Apply -- sets the D3D states controlled by the camera *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/29/2001 gth : Created. *
*=============================================================================================*/
void CameraClass::Apply(void)
{
Update_Frustum();
int width,height,bits;
bool windowed;
WW3D::Get_Render_Target_Resolution(width,height,bits,windowed);
D3DVIEWPORT8 vp;
vp.X = (DWORD)(Viewport.Min.X * (float)width);
vp.Y = (DWORD)(Viewport.Min.Y * (float)height);
vp.Width = (DWORD)((Viewport.Max.X - Viewport.Min.X) * (float)width);
vp.Height = (DWORD)((Viewport.Max.Y - Viewport.Min.Y) * (float)height);
vp.MinZ = ZBufferMin;
vp.MaxZ = ZBufferMax;
DX8Wrapper::Set_Viewport(&vp);
Matrix4x4 d3dprojection;
Get_D3D_Projection_Matrix(&d3dprojection);
DX8Wrapper::Set_Projection_Transform_With_Z_Bias(d3dprojection,ZNear,ZFar);
DX8Wrapper::Set_Transform(D3DTS_VIEW,CameraInvTransform);
}
void CameraClass::Set_Clip_Planes(float znear,float zfar)
{
FrustumValid = false;
ZNear = znear;
ZFar = zfar;
}
void CameraClass::Get_Clip_Planes(float & znear,float & zfar) const
{
znear = ZNear;
zfar = ZFar;
}
float CameraClass::Get_Horizontal_FOV(void) const
{
float width = ViewPlane.Max.X - ViewPlane.Min.X;
return 2*WWMath::Atan2(width,2.0);
}
float CameraClass::Get_Vertical_FOV(void) const
{
float height = ViewPlane.Max.Y - ViewPlane.Min.Y;
return 2*WWMath::Atan2(height,2.0);
}
float CameraClass::Get_Aspect_Ratio(void) const
{
return AspectRatio;
}
void CameraClass::Get_Projection_Matrix(Matrix4x4 * set_tm)
{
WWASSERT(set_tm != NULL);
Update_Frustum();
*set_tm = ProjectionTransform;
}
void CameraClass::Get_D3D_Projection_Matrix(Matrix4x4 * set_tm)
{
WWASSERT(set_tm != NULL);
Update_Frustum();
*set_tm = ProjectionTransform;
/*
** We need to flip the handed-ness of the projection matrix and
** move the z-range to 0<z<1 rather than -1<z<1
*/
float oozdiff = 1.0 / (ZFar - ZNear);
if (Projection == PERSPECTIVE) {
(*set_tm)[2][2] = -(ZFar) * oozdiff;
(*set_tm)[2][3] = -(ZFar*ZNear) * oozdiff;
} else {
(*set_tm)[2][2] = -oozdiff;
(*set_tm)[2][3] = -ZNear * oozdiff;
}
}
void CameraClass::Get_View_Matrix(Matrix3D * set_tm)
{
WWASSERT(set_tm != NULL);
Update_Frustum();
*set_tm = CameraInvTransform;
}
const Matrix4x4 & CameraClass::Get_Projection_Matrix(void)
{
Update_Frustum();
return ProjectionTransform;
}
const Matrix3D & CameraClass::Get_View_Matrix(void)
{
Update_Frustum();
return CameraInvTransform;
}
void CameraClass::Convert_Old(Vector3 &pos)
{
pos.X=(pos.X+1)/2;
pos.Y=(pos.Y+1)/2;
}
float CameraClass::Compute_Projected_Sphere_Radius(float dist,float radius)
{
Vector4 result = ProjectionTransform * Vector4(radius,0.0f,dist,1.0f);
return result.X / result.W;
}

View file

@ -0,0 +1,456 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/camera.h $*
* *
* Org Author:: Greg_h *
* *
* $Author:: Kenny Mitchell *
* *
* $Modtime:: 06/26/02 4:04p $*
* *
* $Revision:: 14 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
*---------------------------------------------------------------------------------------------*
* Functions: *
* CameraClass::Get_Frustum -- returns the frustum of the camera *
* CameraClass::Get_Frustum_Planes -- returns pointer to the array of frustum planes *
* CameraClass::Get_Frustum_Corners -- returns pointer to the array of frustum corners *
* CameraClass::Get_View_Space_Frustum -- returns the view-space frustum for this camera *
* CameraClass::Get_View_Space_Frustum_Planes -- returns the view space clip planes for this *
* CameraClass::Get_View_Space_Frustum_Corners -- returns the corners of the view space frus *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef CAMERA_H
#define CAMERA_H
#include "always.h"
#include "rendobj.h"
#include "plane.h"
#include "frustum.h"
#include "obbox.h"
#include "vector2.h"
#include "matrix4.h"
#include "colmath.h"
class RenderInfoClass;
/**
** ViewportClass
** This class is used to define a "normalized" screen space rectangle for the
** camera to render into. A viewport which filled the entire screen would be
** (0,0) - (1,1) with 0,0 being the upper left and 1,1 being the lower right.
*/
class ViewportClass
{
public:
ViewportClass(void) : Min(0,0), Max(1,1) { }
ViewportClass(const Vector2 & min,const Vector2 & max) : Min(min), Max(max) { }
ViewportClass(const ViewportClass & vp) : Min(vp.Min), Max(vp.Max) { }
float Width(void) const { return Max.X - Min.X; }
float Height(void) const { return Max.Y - Min.Y; }
Vector2 Min;
Vector2 Max;
};
/**
** CameraClass
** This object controls how vertices are transformed from world-space to view
** space, the parameters of the perspective projection, and the viewport
** on screen that the result is mapped into.
**
** Cameras are not "rendered" and do not need to be "added" to a scene. A
** CameraClass is passed into the WW3D::Render(...) function. The reason
** they are render objects is so that they can be inserted onto the bone of
** some animation and move with the animation...
**
** For all of the projection functions (Matrix4x4, ProjectorClass (used by
** decals and texture projections), and CameraClass) I followed the OpenGL
** convention of passing positive distances for your clip planes even though
** in a right-handed coordinate system your z values are negative after
** transformation to camera space. So Set_Clip_Planes expects positive distances
** to your near and far clip planes.
**
** (gth) We should probably separate CameraClass from RenderObjClass... In the
** case where a camera is using a transform from an animation; the programmer
** is usually requesting the transform and plugging it into the camera anyway.
*/
class CameraClass : public RenderObjClass
{
public:
enum ProjectionType
{
PERSPECTIVE = 0,
ORTHO
};
enum ProjectionResType
{
INSIDE_FRUSTUM,
OUTSIDE_FRUSTUM,
OUTSIDE_NEAR_CLIP,
OUTSIDE_FAR_CLIP,
};
CameraClass(void);
CameraClass(const CameraClass & src);
CameraClass & operator = (const CameraClass &);
virtual ~CameraClass(void);
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const { return CLASSID_CAMERA; }
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Rendering, cameras don't "render"
/////////////////////////////////////////////////////////////////////////////
virtual void Render(RenderInfoClass & rinfo) { }
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - "Scene Graph"
// Cameras cache their frustum description, this is invalidated whenever
// the transform/position is changed
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Transform(const Matrix3D &m);
virtual void Set_Position(const Vector3 &v);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Bounding Volumes
/////////////////////////////////////////////////////////////////////////////
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const;
virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const;
///////////////////////////////////////////////////////////////////////////
// Camera parameter control
///////////////////////////////////////////////////////////////////////////
// Depth of the scene.
float Get_Depth(void) const;
// Setting the projection type
void Set_Projection_Type(ProjectionType ptype);
ProjectionType Get_Projection_Type(void);
// Setting the clipping ranges in world space distances
void Set_Clip_Planes(float znear,float zfar);
void Get_Clip_Planes(float & znear,float & zfar) const;
// Setting the zbuffer range used during rendering. (Added to allow subdividing the z-buffer. -MW).
void Set_Zbuffer_Range(float znear,float zfar) {ZBufferMin = znear;ZBufferMax=zfar;}
void Get_Zbuffer_Range(float & znear,float & zfar) const {znear=ZBufferMin;zfar=ZBufferMax;}
// Methods for setting the View Plane.
// NOTE: View plane is always at a distance of 1.0 from the eye.
void Set_View_Plane(const Vector2 & min,const Vector2 & max);
void Set_View_Plane(float hfov,float vfov = -1);
void Set_Aspect_Ratio(float width_to_height);
// Methods for querying the View Plane settings.
void Get_View_Plane(Vector2 & set_min,Vector2 & set_max) const;
float Get_Horizontal_FOV(void) const;
float Get_Vertical_FOV(void) const;
float Get_Aspect_Ratio(void) const;
// Access to the projection matrices for this camera
void Get_Projection_Matrix(Matrix4x4 * set_tm);
void Get_D3D_Projection_Matrix(Matrix4x4 * set_tm);
void Get_View_Matrix(Matrix3D * set_tm);
const Matrix4x4 & Get_Projection_Matrix(void);
const Matrix3D & Get_View_Matrix(void);
// Projecting and Un-Projecting a point
ProjectionResType Project(Vector3 & dest,const Vector3 & ws_point) const;
ProjectionResType Project_Camera_Space_Point(Vector3 & dest,const Vector3 & cam_point) const;
void Un_Project(Vector3 & dest,const Vector2 & view_point) const;
void Transform_To_View_Space(Vector3 & dest,const Vector3 & ws_point) const;
void Rotate_To_View_Space(Vector3 & dest,const Vector3 & ws_vector) const;
// Viewport control
void Set_Viewport(const Vector2 & min,const Vector2 & max);
void Get_Viewport(Vector2 & set_min,Vector2 & set_max) const;
const ViewportClass & Get_Viewport(void) const;
void Set_Depth_Range(float zstart = 0.0f,float zend = 1.0f);
void Get_Depth_Range(float * set_zstart,float * set_zend) const;
// Culling for various bounding volumes. These functions will return true if the
// given primitive is culled (i.e. it is *outside* the view frustum)
bool Cull_Sphere(const SphereClass & sphere) const;
bool Cull_Sphere_On_Frustum_Sides(const SphereClass & sphere) const;
bool Cull_Box(const AABoxClass & box) const;
// Various properties of the camera's frustum: These funcitons return a
// pointer to the internal storage of the descriptions. there will be
// 6 frustum planes, 8 corner points, see the implementations of these
// functions for definitions on which points/planes are associated with
// each index. Better yet, just use the Frustum object.
const FrustumClass & Get_Frustum(void) const;
const PlaneClass * Get_Frustum_Planes(void) const;
const Vector3 * Get_Frustum_Corners(void) const;
const FrustumClass & Get_View_Space_Frustum(void) const;
const PlaneClass * Get_View_Space_Frustum_Planes(void) const;
const Vector3 * Get_View_Space_Frustum_Corners(void) const;
const OBBoxClass & Get_Near_Clip_Bounding_Box(void) const;
// Methods for transforming/projecting points between various coordinate systems
// associated with this camera.
// "Device Space" - pixel coordinate
// "View Space" - 3D space where the view point is at 0,0,0 and the view plane is at z=-1.0
// "World Space" - 3D world coordinate system.
void Device_To_View_Space(const Vector2 & device_coord,Vector3 * view_coord);
void Device_To_World_Space(const Vector2 & device_coord,Vector3 * world_coord);
float Compute_Projected_Sphere_Radius(float dist,float radius);
// apply this camera's settings into d3d.
void Apply(void);
// utility class to convert to old space of 0..1
static void Convert_Old(Vector3 &pos);
protected:
void Update_Frustum(void) const;
ProjectionType Projection; // projection type, orthographic or perspective
ViewportClass Viewport; // pixel viewport to render into
ViewportClass ViewPlane; // corners of a slice through the frustum at z=-1.0
float AspectRatio; // aspect ratio of the camera, width / height
float ZNear; // near clip plane distance
float ZFar; // far clip plane distance
float ZBufferMin; // smallest value we'll write into the z-buffer (usually 0.0)
float ZBufferMax; // largest value we'll write into the z-buffer (usually 1.0)
mutable bool FrustumValid;
mutable FrustumClass Frustum; // world-space frustum and clip planes
mutable FrustumClass ViewSpaceFrustum; // view-space frustum and clip planes
mutable OBBoxClass NearClipBBox; // obbox which bounds the near clip plane
mutable Matrix4x4 ProjectionTransform;
mutable Matrix3D CameraInvTransform;
};
inline float CameraClass::Get_Depth(void) const
{
return ZFar;
}
inline void CameraClass::Set_Projection_Type(ProjectionType ptype)
{
FrustumValid = false;
Projection = ptype;
}
inline CameraClass::ProjectionType CameraClass::Get_Projection_Type(void)
{
return Projection;
}
inline void CameraClass::Set_Viewport(const Vector2 & min,const Vector2 & max)
{
Viewport.Min = min; Viewport.Max = max;
FrustumValid = false;
}
inline void CameraClass::Get_Viewport(Vector2 & set_min,Vector2 & set_max) const
{
set_min = Viewport.Min;
set_max = Viewport.Max;
}
inline void CameraClass::Set_Depth_Range(float zmin,float zmax)
{
ZBufferMin = zmin;
ZBufferMax = zmax;
}
inline void CameraClass::Get_Depth_Range(float * set_zmin,float * set_zmax) const
{
if (set_zmin != NULL) {
*set_zmin = ZBufferMin;
}
if (set_zmax != NULL) {
*set_zmax = ZBufferMax;
}
}
inline const ViewportClass & CameraClass::Get_Viewport(void) const
{
return Viewport;
}
inline bool CameraClass::Cull_Sphere(const SphereClass & sphere) const
{
const FrustumClass & frustum = Get_Frustum();
return CollisionMath::Overlap_Test(frustum,sphere) == CollisionMath::OUTSIDE;
}
inline bool CameraClass::Cull_Sphere_On_Frustum_Sides(const SphereClass & sphere) const
{
const FrustumClass & frustum = Get_Frustum();
const PlaneClass * planes = frustum.Planes;
bool is_visible = true;
for (int i = 1; i < 5; i++) {
is_visible = is_visible && (CollisionMath::Overlap_Test(planes[i],sphere) & (CollisionMath::INSIDE|CollisionMath::BOTH));
}
return !is_visible;
}
/***********************************************************************************************
* CameraClass::Get_Frustum -- returns the frustum of the camera *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 3/24/99 GTH : Created. *
*=============================================================================================*/
inline const FrustumClass &
CameraClass::Get_Frustum(void) const
{
Update_Frustum();
return Frustum;
}
/***********************************************************************************************
* CameraClass::Get_Frustum_Planes -- returns pointer to the array of frustum planes *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/29/98 GTH : Created. *
*=============================================================================================*/
inline const PlaneClass *
CameraClass::Get_Frustum_Planes(void) const
{
const FrustumClass & frustum = Get_Frustum();
return frustum.Planes;
}
/***********************************************************************************************
* CameraClass::Get_Frustum_Corners -- returns pointer to the array of frustum corners *
* *
* The camera frustum corner FrustumCorners are defined in the following order *
* The first four points lie on the near clipping plane: *
* upper left 0, upper right 1, lower left 2, lower right 3. *
* The last four points lie on the far clipping plane, numbered analogous fashion. *
* upper left 4, upper right 5, lower left 6, lower right 7. *
* (remember: the camera space has x going to the right, y up and z backwards). *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/29/98 GTH : Created. *
*=============================================================================================*/
inline const Vector3 *
CameraClass::Get_Frustum_Corners(void) const
{
const FrustumClass & frustum = Get_Frustum();
return frustum.Corners;
}
/***********************************************************************************************
* CameraClass::Get_View_Space_Frustum -- returns the view-space frustum for this camera *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/16/2001 gth : Created. *
*=============================================================================================*/
inline const FrustumClass & CameraClass::Get_View_Space_Frustum(void) const
{
Update_Frustum();
return ViewSpaceFrustum;
}
/***********************************************************************************************
* CameraClass::Get_View_Space_Frustum_Planes -- returns the view space clip planes for this c *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/16/2001 gth : Created. *
*=============================================================================================*/
inline const PlaneClass * CameraClass::Get_View_Space_Frustum_Planes(void) const
{
const FrustumClass & frustum = Get_View_Space_Frustum();
return frustum.Planes;
}
/***********************************************************************************************
* CameraClass::Get_View_Space_Frustum_Corners -- returns the corners of the view space frustu *
* *
* The camera frustum corner FrustumCorners are defined in the following order *
* The first four points lie on the near clipping plane: *
* upper left 0, upper right 1, lower left 2, lower right 3. *
* The last four points lie on the far clipping plane, numbered analogous fashion. *
* upper left 4, upper right 5, lower left 6, lower right 7. *
* (remember: camera space has x going to the right, y up and z backwards). *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/16/2001 gth : Created. *
*=============================================================================================*/
inline const Vector3 * CameraClass::Get_View_Space_Frustum_Corners(void) const
{
const FrustumClass & frustum = Get_View_Space_Frustum();
return frustum.Corners;
}
#endif

View file

@ -0,0 +1,65 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /Commando/Code/ww3d2/classid.h $*
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 3/29/01 1:13a $*
* *
* $Revision:: 2 $*
* *
*-------------------------------------------------------------------------*/
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef CLASSID_H
#define CLASSID_H
#include "always.h"
/*
** enum of all the WW3D class IDs.
*/
enum
{
ID_INDIRECT_TEXTURE_CLASS = 0x10000, // IndirectTextureClass "texture.h"
ID_VARIABLE_TEXTURE_CLASS, // VariableTextureClass "texture.h"
ID_FILE_LIST_TEXTURE_CLASS, // FileListTextureClass "texture.h"
ID_RESIZEABLE_TEXTURE_INSTANCE_CLASS, // ResizeableTextureInstanceClass "texture.h"
ID_ANIM_TEXTURE_INSTANCE_CLASS, // AnimTextureInstanceClass "texture.h"
ID_MANUAL_ANIM_TEXTURE_INSTANCE_CLASS, // ManualAnimTextureInstanceClass "texture.h"
ID_TIME_ANIM_TEXTURE_INSTANCE_CLASS, // TimeAnimTextureInstanceClass "texture.h"
ID_POINT_GROUP_CLASS, // PointGroupClass "pointgr.h"
ID_MESH_MODEL_CLASS, // MeshModelClass "mesh.cpp"
ID_CACHED_TEXTURE_FILE_CLASS, // CachedTextureFileClass "assetmgr.cpp"
ID_STREAMING_TEXTURE_CLASS, // StreamingTextureClass "texture.h"
ID_STREAMING_TEXTURE_INSTANCE_CLASS, // StreamingTextureInstanceClass "texture.h"
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,149 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/collect.h $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 1/08/01 10:04a $*
* *
* $Revision:: 1 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef COLLECT_H
#define COLLECT_H
#include "rendobj.h"
#include "composite.h"
#include "vector.h"
#include "proto.h"
#include "w3d_file.h"
#include "wwstring.h"
#include "proxy.h"
class CollectionDefClass;
class SnapPointsClass;
/*
** CollectionClass
** This is a render object which contains a collection of render objects.
*/
class CollectionClass : public CompositeRenderObjClass
{
public:
CollectionClass(void);
CollectionClass(const CollectionDefClass & def);
CollectionClass(const CollectionClass & src);
CollectionClass & CollectionClass::operator = (const CollectionClass &);
virtual ~CollectionClass(void);
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const;
virtual int Get_Num_Polys(void) const;
/////////////////////////////////////////////////////////////////////////////
// Proxy interface
/////////////////////////////////////////////////////////////////////////////
virtual int Get_Proxy_Count (void) const;
virtual bool Get_Proxy (int index, ProxyClass &proxy) const;
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Rendering
/////////////////////////////////////////////////////////////////////////////
virtual void Render(RenderInfoClass & rinfo);
virtual void Special_Render(SpecialRenderInfoClass & rinfo);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - "Scene Graph"
/////////////////////////////////////////////////////////////////////////////
virtual void Set_Transform(const Matrix3D &m);
virtual void Set_Position(const Vector3 &v);
virtual int Get_Num_Sub_Objects(void) const;
virtual RenderObjClass * Get_Sub_Object(int index) const;
virtual int Add_Sub_Object(RenderObjClass * subobj);
virtual int Remove_Sub_Object(RenderObjClass * robj);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Collision Detection, Ray Tracing
/////////////////////////////////////////////////////////////////////////////
virtual bool Cast_Ray(RayCollisionTestClass & raytest);
virtual bool Cast_AABox(AABoxCollisionTestClass & boxtest);
virtual bool Cast_OBBox(OBBoxCollisionTestClass & boxtest);
virtual bool Intersect_AABox(AABoxIntersectionTestClass & boxtest);
virtual bool Intersect_OBBox(OBBoxIntersectionTestClass & boxtest);
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Bounding Volumes
/////////////////////////////////////////////////////////////////////////////
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const;
virtual void Get_Obj_Space_Bounding_Box(AABoxClass & box) const;
/////////////////////////////////////////////////////////////////////////////
// Render Object Interface - Attributes, Options, Properties, etc
/////////////////////////////////////////////////////////////////////////////
virtual int Snap_Point_Count(void);
virtual void Get_Snap_Point(int index,Vector3 * set);
virtual void Scale(float scale);
virtual void Scale(float scalex, float scaley, float scalez);
virtual void Update_Obj_Space_Bounding_Volumes(void);
protected:
void Free(void);
void Update_Sub_Object_Transforms(void);
DynamicVectorClass <ProxyClass> ProxyList;
DynamicVectorClass <RenderObjClass *> SubObjects;
SnapPointsClass * SnapPoints;
SphereClass BoundSphere;
AABoxClass BoundBox;
};
/*
** CollectionLoaderClass
** Loader for collection objects
*/
class CollectionLoaderClass : public PrototypeLoaderClass
{
public:
virtual int Chunk_Type(void) { return W3D_CHUNK_COLLECTION; }
virtual PrototypeClass * Load_W3D(ChunkLoadClass & cload);
};
extern CollectionLoaderClass _CollectionLoader;
#endif

View file

@ -0,0 +1,161 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Colorspace *
* *
* $Archive:: $*
* *
* Original Author:: Hector Yee *
* *
* $Author:: $*
* *
* $Modtime:: $*
* *
* $Revision:: $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef COLORSPACE_H
#define COLORSPACE_H
#include "dx8wrapper.h"
#include <wwmath.h>
void RGB_To_HSV(Vector3 &hsv,const Vector3 &rgb);
void HSV_To_RGB(Vector3 &rgb, const Vector3 &hsv);
void Recolor(Vector3 &rgb, const Vector3 &hsv_shift);
//---------------------------------------------------------------------
// Color Conversions
//---------------------------------------------------------------------
inline void RGB_To_HSV(Vector3 &hsv,const Vector3 &rgb)
// modified from Foley et al. page 592
// converts rgb[0..1] to h [0,360), s and v in [0,1]
// negative h values are to signify undefined
{
float max=WWMath::Max(rgb.X,rgb.Y);
max=WWMath::Max(max,rgb.Z);
float min=WWMath::Min(rgb.X,rgb.Y);
min=WWMath::Min(min,rgb.Z);
// value
hsv.Z=max;
// saturation
hsv.Y=(max!=0.0f)?((max-min)/max):0.0f;
if (hsv.Y==0.0f) hsv.X=-1.0f;
else
{
float delta=max-min;
if (rgb.X==max)
hsv.X=(rgb.Y-rgb.Z)/delta;
else if (rgb.Y==max)
hsv.X=2.0f+ (rgb.Z-rgb.X)/delta;
else if (rgb.Z==max)
hsv.X=4.0f+ (rgb.X-rgb.Y)/delta;
hsv.X*=60.0f;
if (hsv.X<0.0f) hsv.X+=360.0f;
}
}
inline void HSV_To_RGB(Vector3 &rgb, const Vector3 &hsv)
{
float h=hsv.X;
float s=hsv.Y;
float v=hsv.Z;
if (hsv.Y==0.0f) {
rgb.Set(v,v,v);
} else {
float f,p,q,t;
int i;
if (h==360.0f) h=0.0f;
h/=60.0f;
i=WWMath::Floor(h);
f=h-i;
p=v*(1.0f-s);
q=v*(1.0f-(s*f));
t=v*(1.0f-(s*(1.0f-f)));
switch (i) {
case 0:
rgb.Set(v,t,p);
break;
case 1:
rgb.Set(q,v,p);
break;
case 2:
rgb.Set(p,v,t);
break;
case 3:
rgb.Set(p,q,v);
break;
case 4:
rgb.Set(t,p,v);
break;
case 5:
rgb.Set(v,p,q);
break;
}
}
}
inline void Recolor(Vector3 &rgb, const Vector3 &hsv_shift)
{
Vector3 hsv;
RGB_To_HSV(hsv,rgb);
// If the Hue has the "undefined flag" (a negative value), this means that the color is pure
// monochrome. In this case do not shift the hue (it is undefined) or the saturation (it is 0
// so it cannot be decreased, and increasing it would cause the undefined hue to actually
// become visible). In this case, we only modify the value.
if (hsv.X<0.0f) hsv+=Vector3(0.0f,0.0f,hsv_shift.Z);
else hsv+=hsv_shift;
// angular mod
if (hsv.X<0.0f) hsv.X+=360.0f;
if (hsv.X>360.0f) hsv.X-=360.0f;
// clamp saturation and value
hsv.Y=WWMath::Clamp(hsv.Y,0.0f,1.0f);
hsv.Z=WWMath::Clamp(hsv.Z,0.0f,1.0f);
HSV_To_RGB(rgb,hsv);
}
inline void Recolor(unsigned& rgba, const Vector3 &hsv_shift)
{
Vector4 rgba_v = DX8Wrapper::Convert_Color(rgba);
Recolor((Vector3&)rgba_v, hsv_shift);
rgba = DX8Wrapper::Convert_Color(rgba_v);
}
#endif

View file

@ -0,0 +1,375 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/coltest.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 5/07/01 10:26a $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "coltest.h"
AABoxCollisionTestClass::AABoxCollisionTestClass(const AABoxCollisionTestClass & that) :
CollisionTestClass(that),
Box(that.Box),
Move(that.Move),
SweepMin(that.SweepMin),
SweepMax(that.SweepMax)
{
}
AABoxCollisionTestClass::AABoxCollisionTestClass(const AABoxClass & aabox,const Vector3 & move,CastResultStruct * res,int collision_type) :
CollisionTestClass(res,collision_type),
Box(aabox),
Move(move)
{
SweepMin = Box.Center - Box.Extent;
SweepMax = Box.Center + Box.Extent;
Vector3 endmin = Box.Center + move - Box.Extent;
Vector3 endmax = Box.Center + move + Box.Extent;
if (endmax.X > SweepMax.X) SweepMax.X = endmax.X;
if (endmax.Y > SweepMax.Y) SweepMax.Y = endmax.Y;
if (endmax.Z > SweepMax.Z) SweepMax.Z = endmax.Z;
if (endmin.X < SweepMin.X) SweepMin.X = endmin.X;
if (endmin.Y < SweepMin.Y) SweepMin.Y = endmin.Y;
if (endmin.Z < SweepMin.Z) SweepMin.Z = endmin.Z;
}
bool AABoxCollisionTestClass::Cull(const AABoxClass & box)
{
// const float MOVE_THRESHOLD = 2.0f;
// if (WWMath::Fabs(Move.X) + WWMath::Fabs(Move.Y) + WWMath::Fabs(Move.Z) > MOVE_THRESHOLD) {
// CastResultStruct res;
// return !Box.Cast_To_Box(Move,box,&res);
// } else {
Vector3 min_corner;
Vector3 max_corner;
Vector3::Subtract(box.Center,box.Extent,&min_corner);
Vector3::Add(box.Center,box.Extent,&max_corner);
if ((SweepMin.X > max_corner.X) || (SweepMax.X < min_corner.X)) {
return true;
}
if ((SweepMin.Y > max_corner.Y) || (SweepMax.Y < min_corner.Y)) {
return true;
}
if ((SweepMin.Z > max_corner.Z) || (SweepMax.Z < min_corner.Z)) {
return true;
}
return false;
// }
}
void AABoxCollisionTestClass::Rotate(ROTATION_TYPE rotation)
{
#ifndef NDEBUG
int i;
Matrix3D tm(1);
switch(rotation) {
case ROTATE_NONE:
break;
case ROTATE_Z90:
tm = Matrix3D::RotateZ90;
break;
case ROTATE_Z180:
tm = Matrix3D::RotateZ180;
break;
case ROTATE_Z270:
tm = Matrix3D::RotateZ270;
break;
}
#ifdef ALLOW_TEMPORARIES
Vector3 realcenter = tm * Box.Center;
#else
Vector3 realcenter;
tm.mulVector3(Box.Center, realcenter);
#endif
Vector3 pts[8];
Vector3 & min = SweepMin;
Vector3 & max = SweepMax;
pts[0].Set(min.X,min.Y,min.Z);
pts[1].Set(min.X,max.Y,min.Z);
pts[2].Set(max.X,max.Y,min.Z);
pts[3].Set(max.X,min.Y,min.Z);
pts[4].Set(min.X,min.Y,max.Z);
pts[5].Set(min.X,max.Y,max.Z);
pts[6].Set(max.X,max.Y,max.Z);
pts[7].Set(max.X,min.Y,max.Z);
// for (i=0; i<8; i++) {
// pts[i] = tm * pts[i];
// }
tm.mulVector3Array(pts, 8);
Vector3 realmin = pts[0];
Vector3 realmax = pts[0];
for (i=1; i<8; i++) {
if (realmin.X >= pts[i].X) realmin.X = pts[i].X;
if (realmin.Y >= pts[i].Y) realmin.Y = pts[i].Y;
if (realmin.Z >= pts[i].Z) realmin.Z = pts[i].Z;
if (realmax.X <= pts[i].X) realmax.X = pts[i].X;
if (realmax.Y <= pts[i].Y) realmax.Y = pts[i].Y;
if (realmax.Z <= pts[i].Z) realmax.Z = pts[i].Z;
}
#endif
// rotate the test by the desired rotation about the Z axis, special cased for
// 90 degree rotations about Z. arbitrary rotations cause the axis aligned
// box to not be aligned any more :-)
float tmp,minx,miny,maxx,maxy;
switch(rotation) {
case ROTATE_NONE:
break;
case ROTATE_Z90:
// rotate the center point and the move vector
tmp = Box.Center.X; Box.Center.X = -Box.Center.Y; Box.Center.Y = tmp;
tmp = Move.X; Move.X = -Move.Y; Move.Y = tmp;
// swap x and y for the extent
tmp = Box.Extent.X; Box.Extent.X = Box.Extent.Y; Box.Extent.Y = tmp;
// update sweep bounding box
minx = SweepMin.X; miny = SweepMin.Y; maxx = SweepMax.X; maxy = SweepMax.Y;
SweepMin.X = -maxy;
SweepMin.Y = minx;
SweepMax.X = -miny;
SweepMax.Y = maxx;
break;
case ROTATE_Z180:
// rotate center and move vector
Box.Center.X = -Box.Center.X;
Box.Center.Y = -Box.Center.Y;
Move.X = -Move.X;
Move.Y = -Move.Y;
// update min/max boxes
minx = SweepMin.X; miny = SweepMin.Y; maxx = SweepMax.X; maxy = SweepMax.Y;
SweepMin.X = -maxx;
SweepMin.Y = -maxy;
SweepMax.X = -minx;
SweepMax.Y = -miny;
break;
case ROTATE_Z270:
// rotate center and move.
tmp = Box.Center.X; Box.Center.X = Box.Center.Y; Box.Center.Y = -tmp;
tmp = Move.X; Move.X = Move.Y; Move.Y = -tmp;
// update extent (x and y axis swap)
tmp = Box.Extent.X; Box.Extent.X = Box.Extent.Y; Box.Extent.Y = tmp;
// update min/max boxes
minx = SweepMin.X; miny = SweepMin.Y; maxx = SweepMax.X; maxy = SweepMax.Y;
SweepMin.X = miny;
SweepMin.Y = -maxx;
SweepMax.X = maxy;
SweepMax.Y = -minx;
break;
}
#ifndef NDEBUG
assert((Box.Center - realcenter).Length() < 0.001f);
assert((SweepMin - realmin).Length() < 0.001f);
assert((SweepMax - realmax).Length() < 0.001f);
#endif
}
void AABoxCollisionTestClass::Transform(const Matrix3D & tm)
{
// NOTE: this function will expand the box to enclose the rotated
// form of the original box. In practice, this function was only
// implemented to double-check the results of the Translate and Rotate
// functions.
int i;
Vector3 tmpcenter = Box.Center;
Vector3 tmpextent = Box.Extent;
tm.Transform_Center_Extent_AABox(tmpcenter,tmpextent,&Box.Center,&Box.Extent);
Move = tm.Rotate_Vector(Move);
Vector3 pts[8];
Vector3 & min = SweepMin;
Vector3 & max = SweepMax;
pts[0].Set(min.X,min.Y,min.Z);
pts[1].Set(min.X,max.Y,min.Z);
pts[2].Set(max.X,max.Y,min.Z);
pts[3].Set(max.X,min.Y,min.Z);
pts[4].Set(min.X,min.Y,max.Z);
pts[5].Set(min.X,max.Y,max.Z);
pts[6].Set(max.X,max.Y,max.Z);
pts[7].Set(max.X,min.Y,max.Z);
// for (i=0; i<8; i++) {
// pts[i] = tm * pts[i];
// }
tm.mulVector3Array(pts, 8);
Vector3 realmin = pts[0];
Vector3 realmax = pts[0];
for (i=1; i<8; i++) {
if (realmin.X >= pts[i].X) realmin.X = pts[i].X;
if (realmin.Y >= pts[i].Y) realmin.Y = pts[i].Y;
if (realmin.Z >= pts[i].Z) realmin.Z = pts[i].Z;
if (realmax.X <= pts[i].X) realmax.X = pts[i].X;
if (realmax.Y <= pts[i].Y) realmax.Y = pts[i].Y;
if (realmax.Z <= pts[i].Z) realmax.Z = pts[i].Z;
}
SweepMin = realmin;
SweepMax = realmax;
}
OBBoxCollisionTestClass::OBBoxCollisionTestClass
(
const OBBoxClass & obbox,
const Vector3 & move,
CastResultStruct * res,
int type
) :
CollisionTestClass(res,type),
Box(obbox),
Move(move)
{
Vector3 max_extent;
max_extent.X = WWMath::Fabs(Box.Basis[0][0] * Box.Extent.X) +
WWMath::Fabs(Box.Basis[0][1] * Box.Extent.Y) +
WWMath::Fabs(Box.Basis[0][2] * Box.Extent.Z) + 0.01f;
max_extent.Y = WWMath::Fabs(Box.Basis[1][0] * Box.Extent.X) +
WWMath::Fabs(Box.Basis[1][1] * Box.Extent.Y) +
WWMath::Fabs(Box.Basis[1][2] * Box.Extent.Z) + 0.01f;
max_extent.Z = WWMath::Fabs(Box.Basis[2][0] * Box.Extent.X) +
WWMath::Fabs(Box.Basis[2][1] * Box.Extent.Y) +
WWMath::Fabs(Box.Basis[2][2] * Box.Extent.Z) + 0.01f;
SweepMin = Box.Center - max_extent;
SweepMax = Box.Center + max_extent;
Vector3 endmin = Box.Center + move - max_extent;
Vector3 endmax = Box.Center + move + max_extent;
if (endmax.X > SweepMax.X) SweepMax.X = endmax.X;
if (endmax.Y > SweepMax.Y) SweepMax.Y = endmax.Y;
if (endmax.Z > SweepMax.Z) SweepMax.Z = endmax.Z;
if (endmin.X < SweepMin.X) SweepMin.X = endmin.X;
if (endmin.Y < SweepMin.Y) SweepMin.Y = endmin.Y;
if (endmin.Z < SweepMin.Z) SweepMin.Z = endmin.Z;
}
OBBoxCollisionTestClass::OBBoxCollisionTestClass(const OBBoxCollisionTestClass & that) :
CollisionTestClass(that),
Box(that.Box),
Move(that.Move),
SweepMin(that.SweepMin),
SweepMax(that.SweepMax)
{
}
OBBoxCollisionTestClass::OBBoxCollisionTestClass
(
const OBBoxCollisionTestClass & that,
const Matrix3D & tm
) :
CollisionTestClass(that)
{
tm.Transform_Min_Max_AABox(that.SweepMin,that.SweepMax,&SweepMin,&SweepMax);
Matrix3D::Rotate_Vector(tm,that.Move,&Move);
OBBoxClass::Transform(tm,that.Box,&Box);
}
OBBoxCollisionTestClass::OBBoxCollisionTestClass
(
const AABoxCollisionTestClass & that,
const Matrix3D & tm
) :
CollisionTestClass(that)
{
tm.Transform_Min_Max_AABox(that.SweepMin,that.SweepMax,&SweepMin,&SweepMax);
Matrix3D::Rotate_Vector(tm,that.Move,&Move);
Matrix3D::Transform_Vector(tm,that.Box.Center,&(Box.Center));
Box.Extent = that.Box.Extent;
Box.Basis = tm; // copies the 3x3 rotation portion of the transform
}
bool OBBoxCollisionTestClass::Cull(const AABoxClass & box)
{
Vector3 min_corner;
Vector3 max_corner;
Vector3::Subtract(box.Center,box.Extent,&min_corner);
Vector3::Add(box.Center,box.Extent,&max_corner);
if ((SweepMin.X > max_corner.X) || (SweepMax.X < min_corner.X)) {
return true;
}
if ((SweepMin.Y > max_corner.Y) || (SweepMax.Y < min_corner.Y)) {
return true;
}
if ((SweepMin.Z > max_corner.Z) || (SweepMax.Z < min_corner.Z)) {
return true;
}
return false;
}

Some files were not shown because too many files have changed in this diff Show more