Repository: Arsunt/TR2Main
Branch: master
Commit: f38bc90438e2
Files: 212
Total size: 1.8 MB
Directory structure:
gitextract_vwbsridj/
├── .gitignore
├── 3dsystem/
│ ├── 3d_gen.cpp
│ ├── 3d_gen.h
│ ├── 3d_out.cpp
│ ├── 3d_out.h
│ ├── 3dinsert.cpp
│ ├── 3dinsert.h
│ ├── phd_math.cpp
│ ├── phd_math.h
│ ├── scalespr.cpp
│ └── scalespr.h
├── CHANGELOG.md
├── COPYING.md
├── Doxyfile
├── README.md
├── TR2Main.cbp
├── TR2Main.cpp
├── TR2Main.rc
├── TR2_progress.txt
├── binaries/
│ ├── BAREFOOT.SFX
│ └── README.md
├── configs/
│ ├── TR2Main.json
│ └── textures/
│ └── texpages/
│ ├── README.md
│ └── config.json
├── embedded/
│ ├── BUTTONS.JSON
│ └── BUTTONS.PCX
├── game/
│ ├── bear.cpp
│ ├── bear.h
│ ├── bird.cpp
│ ├── bird.h
│ ├── boat.cpp
│ ├── boat.h
│ ├── box.cpp
│ ├── box.h
│ ├── camera.cpp
│ ├── camera.h
│ ├── cinema.cpp
│ ├── cinema.h
│ ├── collide.cpp
│ ├── collide.h
│ ├── control.cpp
│ ├── control.h
│ ├── demo.cpp
│ ├── demo.h
│ ├── diver.cpp
│ ├── diver.h
│ ├── dog.cpp
│ ├── dog.h
│ ├── dragon.cpp
│ ├── dragon.h
│ ├── draw.cpp
│ ├── draw.h
│ ├── eel.cpp
│ ├── eel.h
│ ├── effects.cpp
│ ├── effects.h
│ ├── enemies.cpp
│ ├── enemies.h
│ ├── gameflow.cpp
│ ├── gameflow.h
│ ├── hair.cpp
│ ├── hair.h
│ ├── health.cpp
│ ├── health.h
│ ├── inventory.cpp
│ ├── inventory.h
│ ├── invfunc.cpp
│ ├── invfunc.h
│ ├── invtext.cpp
│ ├── invtext.h
│ ├── items.cpp
│ ├── items.h
│ ├── lara.cpp
│ ├── lara.h
│ ├── lara1gun.cpp
│ ├── lara1gun.h
│ ├── lara2gun.cpp
│ ├── lara2gun.h
│ ├── laraclimb.cpp
│ ├── laraclimb.h
│ ├── larafire.cpp
│ ├── larafire.h
│ ├── laraflare.cpp
│ ├── laraflare.h
│ ├── laramisc.cpp
│ ├── laramisc.h
│ ├── larasurf.cpp
│ ├── larasurf.h
│ ├── laraswim.cpp
│ ├── laraswim.h
│ ├── lot.cpp
│ ├── lot.h
│ ├── missile.cpp
│ ├── missile.h
│ ├── moveblock.cpp
│ ├── moveblock.h
│ ├── objects.cpp
│ ├── objects.h
│ ├── people.cpp
│ ├── people.h
│ ├── pickup.cpp
│ ├── pickup.h
│ ├── rat.cpp
│ ├── rat.h
│ ├── savegame.cpp
│ ├── savegame.h
│ ├── setup.cpp
│ ├── setup.h
│ ├── shark.cpp
│ ├── shark.h
│ ├── skidoo.cpp
│ ├── skidoo.h
│ ├── sound.cpp
│ ├── sound.h
│ ├── sphere.cpp
│ ├── sphere.h
│ ├── spider.cpp
│ ├── spider.h
│ ├── text.cpp
│ ├── text.h
│ ├── traps.cpp
│ ├── traps.h
│ ├── wolf.cpp
│ ├── wolf.h
│ ├── yeti.cpp
│ └── yeti.h
├── global/
│ ├── md5.c
│ ├── md5.h
│ ├── memmem.c
│ ├── memmem.h
│ ├── precompiled.h
│ ├── resource.h
│ ├── types.h
│ └── vars.h
├── json-parser/
│ ├── LICENSE
│ ├── README.md
│ ├── json.c
│ └── json.h
├── modding/
│ ├── background_new.cpp
│ ├── background_new.h
│ ├── cd_pauld.cpp
│ ├── cd_pauld.h
│ ├── file_utils.cpp
│ ├── file_utils.h
│ ├── gdi_utils.cpp
│ ├── gdi_utils.h
│ ├── joy_output.cpp
│ ├── joy_output.h
│ ├── json_utils.cpp
│ ├── json_utils.h
│ ├── mod_utils.cpp
│ ├── mod_utils.h
│ ├── pause.cpp
│ ├── pause.h
│ ├── psx_bar.cpp
│ ├── psx_bar.h
│ ├── raw_input.cpp
│ ├── raw_input.h
│ ├── texture_utils.cpp
│ ├── texture_utils.h
│ ├── xinput_ex.cpp
│ └── xinput_ex.h
└── specific/
├── background.cpp
├── background.h
├── display.cpp
├── display.h
├── file.cpp
├── file.h
├── fmv.cpp
├── fmv.h
├── frontend.cpp
├── frontend.h
├── game.cpp
├── game.h
├── hwr.cpp
├── hwr.h
├── init.cpp
├── init.h
├── init_3d.cpp
├── init_3d.h
├── init_display.cpp
├── init_display.h
├── init_input.cpp
├── init_input.h
├── init_sound.cpp
├── init_sound.h
├── input.cpp
├── input.h
├── option.cpp
├── option.h
├── output.cpp
├── output.h
├── registry.cpp
├── registry.h
├── screenshot.cpp
├── screenshot.h
├── setupdlg.cpp
├── setupdlg.h
├── setupwnd.cpp
├── setupwnd.h
├── smain.cpp
├── smain.h
├── sndpc.cpp
├── sndpc.h
├── texture.cpp
├── texture.h
├── utils.cpp
├── utils.h
├── winmain.cpp
├── winmain.h
├── winvid.cpp
└── winvid.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.exe
*.dll
*.lib
*.exp
*.gch
*.layout
*.depend
*.cscope_file_list
cscope.out
obj/
bin/
doc/
================================================
FILE: 3dsystem/3d_gen.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "3dsystem/3d_gen.h"
#include "3dsystem/3d_out.h"
#include "3dsystem/3dinsert.h"
#include "3dsystem/phd_math.h"
#include "3dsystem/scalespr.h"
#include "specific/hwr.h"
#include "global/vars.h"
// related to POLYTYPE enum
static void (__cdecl *PolyDrawRoutines[])(__int16 *) = {
draw_poly_gtmap, // gouraud shaded poly (texture)
draw_poly_wgtmap, // gouraud shaded poly (texture + colorkey)
draw_poly_gtmap_persp, // gouraud shaded poly (texture + perspective)
draw_poly_wgtmap_persp, // gouraud shaded poly (texture + colorkey + perspective)
draw_poly_line, // line (color)
draw_poly_flat, // flat shaded poly (color)
draw_poly_gouraud, // gouraud shaded poly (color)
draw_poly_trans, // shadow poly (color + semitransparent)
draw_scaled_spriteC // scaled sprite (texture + colorkey)
};
#if defined(FEATURE_EXTENDED_LIMITS) || defined(FEATURE_VIEW_IMPROVED)
SORT_ITEM SortBuffer[16000];
__int16 Info3dBuffer[480000];
#endif // defined(FEATURE_EXTENDED_LIMITS) || defined(FEATURE_VIEW_IMPROVED)
#ifdef FEATURE_EXTENDED_LIMITS
PHD_SPRITE PhdSpriteInfo[2048];
D3DTLVERTEX HWR_VertexBuffer[0x8000];
#endif // FEATURE_EXTENDED_LIMITS
#ifdef FEATURE_VIEW_IMPROVED
bool PsxFovEnabled;
// view distance
double ViewDistanceFactor = 6.0;
// regular fog
double FogBeginFactor = 1.0;
double FogEndFactor = 6.0;
int FogBeginDepth = DEPTHQ_START;
int FogEndDepth = DEPTHQ_END;
// underwater fog
double WaterFogBeginFactor = 0.6;
double WaterFogEndFactor = 1.0;
int WaterFogBeginDepth = DEPTHQ_START;
int WaterFogEndDepth = DEPTHQ_END;
// fog formula
int CalculateFogShade(int depth) {
int fogBegin, fogEnd;
if( IsWaterEffect ) {
fogBegin = WaterFogBeginDepth;
fogEnd = WaterFogEndDepth;
} else {
fogBegin = FogBeginDepth;
fogEnd = FogEndDepth;
}
if( depth < fogBegin )
return 0;
if( depth >= fogEnd )
return 0x1FFF;
return (depth - fogBegin) * 0x1FFF / (fogEnd - fogBegin);
}
#endif // FEATURE_VIEW_IMPROVED
#ifdef FEATURE_VIDEOFX_IMPROVED
#include "modding/mod_utils.h"
extern DWORD ReflectionMode;
static POLYFILTER ReflectFilter;
static D3DCOLOR ReflectTint = 0;
static bool IsReflect = false;
void ClearMeshReflectState() {
memset(&ReflectFilter, 0 , sizeof(ReflectFilter));
ReflectTint = RGBA_MAKE(0xFF,0xFF,0xFF,0x80);
IsReflect = false;
}
void SetMeshReflectState(int objID, int meshIdx) {
// Clear poly filters and disable reflection by default
ClearMeshReflectState();
if( TextureFormat.bpp < 16 || !ReflectionMode ) return;
if( objID == ID_NONE ) {
// Reflect all meshes with custom tint instead of mesh index
ReflectTint = meshIdx;
IsReflect = true;
return;
}
#ifdef FEATURE_MOD_CONFIG
// Check if config is presented
if( IsModReflectConfigLoaded() ) {
POLYFILTER_NODE *node = NULL;
if( meshIdx < 0 ) {
for( node = GetModReflectStaticsFilter(); node != NULL; node = node->next ) {
if( node->id == objID ) {
ReflectFilter = node->filter;
IsReflect = true;
break;
}
}
} else if( objID >= 0 && objID < ID_NUMBER_OBJECTS ) {
POLYFILTER_NODE **obj = GetModReflectObjectsFilter();
for( node = obj[objID]; node != NULL; node = node->next ) {
if( node->id == meshIdx ) {
ReflectFilter = node->filter;
IsReflect = true;
break;
}
}
}
return;
}
#endif // FEATURE_MOD_CONFIG
// If config is absent or disabled, use hardcoded params
if( objID >= 0 && meshIdx < 0 ) {
// This is static object mesh
return;
}
// This is animated object mesh
switch( objID ) {
case ID_SKIDOO_FAST :
// This one is a fast showmobile from the Golden Mask
// Reflect the windshield only (skidoo body is mesh #0)
if( meshIdx == 0 ) {
// Set filter conditions
ReflectFilter.n_vtx = 59;
ReflectFilter.n_gt4 = 14;
ReflectFilter.n_gt3 = 73;
ReflectFilter.n_g4 = 0;
ReflectFilter.n_g3 = 17;
// All colored triangles are reflective
// The only reflective textured triangle is 48
ReflectFilter.gt3[0].idx = 48;
ReflectFilter.gt3[0].num = 1;
// Quads are not reflective
ReflectFilter.gt4[0].idx = ~0;
ReflectFilter.g4[0].idx = ~0;
IsReflect = true;
}
break;
case ID_SKIDOO_ARMED :
// This one is an armed showmobile
// Reflect the windshield only (skidoo body is mesh #0)
if( meshIdx == 0 ) {
// Set filter conditions
ReflectFilter.n_vtx = 88;
ReflectFilter.n_gt4 = 45;
ReflectFilter.n_gt3 = 60;
ReflectFilter.n_g4 = 0;
ReflectFilter.n_g3 = 0;
// The reflective textured quads are 21..22, 34..47
ReflectFilter.gt4[0].idx = 21;
ReflectFilter.gt4[0].num = 2;
ReflectFilter.gt3[0].idx = 34;
ReflectFilter.gt3[0].num = 14;
// Other polys are not reflective
ReflectFilter.g4[0].idx = ~0;
ReflectFilter.g3[0].idx = ~0;
IsReflect = true;
}
break;
case ID_WORKER5 :
// Reflect the black glass mask of flamethrower buddy (his head is mesh #15)
if( meshIdx == 15 ) {
// Set filter conditions
ReflectFilter.n_vtx = 38;
ReflectFilter.n_gt4 = 30;
ReflectFilter.n_gt3 = 12;
ReflectFilter.n_g4 = 0;
ReflectFilter.n_g3 = 0;
// The reflective textured quads are 22..26
ReflectFilter.gt4[0].idx = 22;
ReflectFilter.gt4[0].num = 5;
// Other polys are not reflective
ReflectFilter.gt3[0].idx = ~0;
ReflectFilter.g4[0].idx = ~0;
ReflectFilter.g3[0].idx = ~0;
IsReflect = true;
}
break;
case ID_SPINNING_BLADE :
if( meshIdx == 0 ) {
// Reflect only quads, not triangles
ReflectFilter.gt3[0].idx = ~0;
ReflectFilter.g3[0].idx = ~0;
IsReflect = true;
}
break;
case ID_BLADE :
// Reflect the blade only (mesh #1)
if( meshIdx == 1 ) {
IsReflect = true;
}
break;
case ID_KILLER_STATUE :
// Reflect the sword only (mesh #7)
if( meshIdx == 7 ) {
IsReflect = true;
}
break;
}
}
static bool InsertEnvmap(__int16 *ptrObj, int vtxCount, bool colored, LPVOID param) {
InsertObjectEM(ptrObj, vtxCount, ReflectTint, (PHD_UV *)param);
return true;
}
static void phd_PutEnvmapPolygons(__int16 *ptrEnv) {
if( ptrEnv == NULL || !IsReflect
|| SavedAppSettings.RenderMode != RM_Hardware ) return;
__int16 *ptrObj = ptrEnv;
ptrObj += 5; // skip x, y, z, radius, flags
__int16 num = *(ptrObj++); // get vertex counter
ptrObj += num * 3; // skip vertices
int vtxCount = *ptrObj++;
if( vtxCount <= 0 ) return;
PHD_UV *uv = new PHD_UV[vtxCount];
for( int i = 0; i < vtxCount; ++i ) {
// make sure that reflection will be drawn after normal poly
PhdVBuf[i].zv -= (double)(W2V_SCALE/2);
// set lighting that depends only from fog distance
PhdVBuf[i].g = 0x1000;
int depth = PhdMatrixPtr->_23 >> W2V_SHIFT;
#ifdef FEATURE_VIEW_IMPROVED
PhdVBuf[i].g += CalculateFogShade(depth);
#else // !FEATURE_VIEW_IMPROVED
if( depth > DEPTHQ_START ) // fog begin
PhdVBuf[i].g += depth - DEPTHQ_START;
#endif // FEATURE_VIEW_IMPROVED
CLAMP(PhdVBuf[i].g, 0x1000, 0x1FFF); // reflection can be darker but not brighter
// rotate normal vectors for X/Y, no translation
int x = (PhdMatrixPtr->_00 * ptrObj[0] +
PhdMatrixPtr->_01 * ptrObj[1] +
PhdMatrixPtr->_02 * ptrObj[2]) >> W2V_SHIFT;
int y = (PhdMatrixPtr->_10 * ptrObj[0] +
PhdMatrixPtr->_11 * ptrObj[1] +
PhdMatrixPtr->_12 * ptrObj[2]) >> W2V_SHIFT;
CLAMP(x, -PHD_IONE, PHD_IONE);
CLAMP(y, -PHD_IONE, PHD_IONE);
uv[i].u = PHD_ONE/PHD_IONE * (x + PHD_IONE)/2;
uv[i].v = PHD_ONE/PHD_IONE * (y + PHD_IONE)/2;
ptrObj += 3;
}
EnumeratePolys(ptrEnv, false, InsertEnvmap, &ReflectFilter, (LPVOID)uv);
delete[] uv;
}
#endif // FEATURE_VIDEOFX_IMPROVED
void phd_GenerateW2V(PHD_3DPOS *viewPos) {
int sx = phd_sin(viewPos->rotX);
int cx = phd_cos(viewPos->rotX);
int sy = phd_sin(viewPos->rotY);
int cy = phd_cos(viewPos->rotY);
int sz = phd_sin(viewPos->rotZ);
int cz = phd_cos(viewPos->rotZ);
PhdMatrixPtr = &MatrixStack[0]; // set matrix stack pointer to W2V
MatrixW2V._00 = PhdMatrixPtr->_00 = TRIGMULT3(sx, sy, sz) + TRIGMULT2(cy, cz);
MatrixW2V._01 = PhdMatrixPtr->_01 = TRIGMULT2(cx, sz);
MatrixW2V._02 = PhdMatrixPtr->_02 = TRIGMULT3(sx, cy, sz) - TRIGMULT2(sy, cz);
MatrixW2V._10 = PhdMatrixPtr->_10 = (int)(FltViewAspect * (double)(TRIGMULT3(sx, sy, cz) - TRIGMULT2(cy, sz)));
MatrixW2V._11 = PhdMatrixPtr->_11 = (int)(FltViewAspect * (double)(TRIGMULT2(cx, cz)));
MatrixW2V._12 = PhdMatrixPtr->_12 = (int)(FltViewAspect * (double)(TRIGMULT3(sx, cy, cz) + TRIGMULT2(sy, sz)));
MatrixW2V._20 = PhdMatrixPtr->_20 = TRIGMULT2(cx, sy);
MatrixW2V._21 = PhdMatrixPtr->_21 = -(sx);
MatrixW2V._22 = PhdMatrixPtr->_22 = TRIGMULT2(cx, cy);
MatrixW2V._03 = PhdMatrixPtr->_03 = viewPos->x;;
MatrixW2V._13 = PhdMatrixPtr->_13 = viewPos->y;
MatrixW2V._23 = PhdMatrixPtr->_23 = viewPos->z;
}
void __cdecl phd_LookAt(int xsrc, int ysrc, int zsrc, int xtar, int ytar, int ztar, __int16 roll) {
PHD_3DPOS viewPos;
VECTOR_ANGLES angles;
phd_GetVectorAngles(xtar - xsrc, ytar - ysrc, ztar - zsrc, &angles);
viewPos.x = xsrc;
viewPos.y = ysrc;
viewPos.z = zsrc;
viewPos.rotX = angles.pitch;
viewPos.rotY = angles.yaw;
viewPos.rotZ = roll;
phd_GenerateW2V(&viewPos);
}
void __cdecl phd_GetVectorAngles(int x, int y, int z, VECTOR_ANGLES *angles) {
__int16 pitch;
angles->yaw = phd_atan(z, x);
while( (__int16)x != x || (__int16)y != y || (__int16)z != z ) {
x >>= 2;
y >>= 2;
z >>= 2;
}
pitch = phd_atan(phd_sqrt(SQR(x) + SQR(z)), y);
if( (y > 0 && pitch > 0) || (y < 0 && pitch < 0) )
pitch = -pitch;
angles->pitch = pitch;
}
void __cdecl phd_RotX(__int16 angle) {
if( angle != 0 ) {
int m0, m1;
int sx = phd_sin(angle);
int cx = phd_cos(angle);
m0 = PhdMatrixPtr->_01 * cx + PhdMatrixPtr->_02 * sx;
m1 = PhdMatrixPtr->_02 * cx - PhdMatrixPtr->_01 * sx;
PhdMatrixPtr->_01 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_02 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_11 * cx + PhdMatrixPtr->_12 * sx;
m1 = PhdMatrixPtr->_12 * cx - PhdMatrixPtr->_11 * sx;
PhdMatrixPtr->_11 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_12 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_21 * cx + PhdMatrixPtr->_22 * sx;
m1 = PhdMatrixPtr->_22 * cx - PhdMatrixPtr->_21 * sx;
PhdMatrixPtr->_21 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_22 = m1 >> W2V_SHIFT;
}
}
void __cdecl phd_RotY(__int16 angle) {
if( angle != 0 ) {
int m0, m1;
int sy = phd_sin(angle);
int cy = phd_cos(angle);
m0 = PhdMatrixPtr->_00 * cy - PhdMatrixPtr->_02 * sy;
m1 = PhdMatrixPtr->_02 * cy + PhdMatrixPtr->_00 * sy;
PhdMatrixPtr->_00 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_02 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_10 * cy - PhdMatrixPtr->_12 * sy;
m1 = PhdMatrixPtr->_12 * cy + PhdMatrixPtr->_10 * sy;
PhdMatrixPtr->_10 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_12 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_20 * cy - PhdMatrixPtr->_22 * sy;
m1 = PhdMatrixPtr->_22 * cy + PhdMatrixPtr->_20 * sy;
PhdMatrixPtr->_20 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_22 = m1 >> W2V_SHIFT;
}
}
void __cdecl phd_RotZ(__int16 angle) {
if( angle != 0 ) {
int m0, m1;
int sz = phd_sin(angle);
int cz = phd_cos(angle);
m0 = PhdMatrixPtr->_00 * cz + PhdMatrixPtr->_01 * sz;
m1 = PhdMatrixPtr->_01 * cz - PhdMatrixPtr->_00 * sz;
PhdMatrixPtr->_00 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_01 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_10 * cz + PhdMatrixPtr->_11 * sz;
m1 = PhdMatrixPtr->_11 * cz - PhdMatrixPtr->_10 * sz;
PhdMatrixPtr->_10 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_11 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_20 * cz + PhdMatrixPtr->_21 * sz;
m1 = PhdMatrixPtr->_21 * cz - PhdMatrixPtr->_20 * sz;
PhdMatrixPtr->_20 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_21 = m1 >> W2V_SHIFT;
}
}
void __cdecl phd_RotYXZ(__int16 ry, __int16 rx, __int16 rz) {
int sx, cx;
int sy, cy;
int sz, cz;
int m0, m1;
if( ry != 0 ) {
sy = phd_sin(ry);
cy = phd_cos(ry);
m0 = PhdMatrixPtr->_00 * cy - PhdMatrixPtr->_02 * sy;
m1 = PhdMatrixPtr->_02 * cy + PhdMatrixPtr->_00 * sy;
PhdMatrixPtr->_00 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_02 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_10 * cy - PhdMatrixPtr->_12 * sy;
m1 = PhdMatrixPtr->_12 * cy + PhdMatrixPtr->_10 * sy;
PhdMatrixPtr->_10 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_12 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_20 * cy - PhdMatrixPtr->_22 * sy;
m1 = PhdMatrixPtr->_22 * cy + PhdMatrixPtr->_20 * sy;
PhdMatrixPtr->_20 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_22 = m1 >> W2V_SHIFT;
}
if( rx != 0 ) {
sx = phd_sin(rx);
cx = phd_cos(rx);
m0 = PhdMatrixPtr->_01 * cx + PhdMatrixPtr->_02 * sx;
m1 = PhdMatrixPtr->_02 * cx - PhdMatrixPtr->_01 * sx;
PhdMatrixPtr->_01 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_02 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_11 * cx + PhdMatrixPtr->_12 * sx;
m1 = PhdMatrixPtr->_12 * cx - PhdMatrixPtr->_11 * sx;
PhdMatrixPtr->_11 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_12 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_21 * cx + PhdMatrixPtr->_22 * sx;
m1 = PhdMatrixPtr->_22 * cx - PhdMatrixPtr->_21 * sx;
PhdMatrixPtr->_21 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_22 = m1 >> W2V_SHIFT;
}
if( rz != 0 ) {
sz = phd_sin(rz);
cz = phd_cos(rz);
m0 = PhdMatrixPtr->_00 * cz + PhdMatrixPtr->_01 * sz;
m1 = PhdMatrixPtr->_01 * cz - PhdMatrixPtr->_00 * sz;
PhdMatrixPtr->_00 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_01 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_10 * cz + PhdMatrixPtr->_11 * sz;
m1 = PhdMatrixPtr->_11 * cz - PhdMatrixPtr->_10 * sz;
PhdMatrixPtr->_10 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_11 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_20 * cz + PhdMatrixPtr->_21 * sz;
m1 = PhdMatrixPtr->_21 * cz - PhdMatrixPtr->_20 * sz;
PhdMatrixPtr->_20 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_21 = m1 >> W2V_SHIFT;
}
}
void __cdecl phd_RotYXZpack(DWORD rpack) {
int sx, cx;
int sy, cy;
int sz, cz;
int m0, m1;
__int16 rx = ((rpack >> 20) & 0x3FF) << 6;
__int16 ry = ((rpack >> 10) & 0x3FF) << 6;
__int16 rz = ((rpack >> 00) & 0x3FF) << 6;
if( ry != 0 ) {
sy = phd_sin(ry);
cy = phd_cos(ry);
m0 = PhdMatrixPtr->_00 * cy - PhdMatrixPtr->_02 * sy;
m1 = PhdMatrixPtr->_02 * cy + PhdMatrixPtr->_00 * sy;
PhdMatrixPtr->_00 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_02 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_10 * cy - PhdMatrixPtr->_12 * sy;
m1 = PhdMatrixPtr->_12 * cy + PhdMatrixPtr->_10 * sy;
PhdMatrixPtr->_10 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_12 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_20 * cy - PhdMatrixPtr->_22 * sy;
m1 = PhdMatrixPtr->_22 * cy + PhdMatrixPtr->_20 * sy;
PhdMatrixPtr->_20 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_22 = m1 >> W2V_SHIFT;
}
if( rx != 0 ) {
sx = phd_sin(rx);
cx = phd_cos(rx);
m0 = PhdMatrixPtr->_01 * cx + PhdMatrixPtr->_02 * sx;
m1 = PhdMatrixPtr->_02 * cx - PhdMatrixPtr->_01 * sx;
PhdMatrixPtr->_01 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_02 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_11 * cx + PhdMatrixPtr->_12 * sx;
m1 = PhdMatrixPtr->_12 * cx - PhdMatrixPtr->_11 * sx;
PhdMatrixPtr->_11 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_12 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_21 * cx + PhdMatrixPtr->_22 * sx;
m1 = PhdMatrixPtr->_22 * cx - PhdMatrixPtr->_21 * sx;
PhdMatrixPtr->_21 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_22 = m1 >> W2V_SHIFT;
}
if( rz != 0 ) {
sz = phd_sin(rz);
cz = phd_cos(rz);
m0 = PhdMatrixPtr->_00 * cz + PhdMatrixPtr->_01 * sz;
m1 = PhdMatrixPtr->_01 * cz - PhdMatrixPtr->_00 * sz;
PhdMatrixPtr->_00 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_01 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_10 * cz + PhdMatrixPtr->_11 * sz;
m1 = PhdMatrixPtr->_11 * cz - PhdMatrixPtr->_10 * sz;
PhdMatrixPtr->_10 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_11 = m1 >> W2V_SHIFT;
m0 = PhdMatrixPtr->_20 * cz + PhdMatrixPtr->_21 * sz;
m1 = PhdMatrixPtr->_21 * cz - PhdMatrixPtr->_20 * sz;
PhdMatrixPtr->_20 = m0 >> W2V_SHIFT;
PhdMatrixPtr->_21 = m1 >> W2V_SHIFT;
}
}
BOOL __cdecl phd_TranslateRel(int x, int y, int z) {
PhdMatrixPtr->_03 += PhdMatrixPtr->_00 * x + PhdMatrixPtr->_01 * y + PhdMatrixPtr->_02 * z;
PhdMatrixPtr->_13 += PhdMatrixPtr->_10 * x + PhdMatrixPtr->_11 * y + PhdMatrixPtr->_12 * z;
PhdMatrixPtr->_23 += PhdMatrixPtr->_20 * x + PhdMatrixPtr->_21 * y + PhdMatrixPtr->_22 * z;
if( ABS(PhdMatrixPtr->_03) > PhdFarZ ||
ABS(PhdMatrixPtr->_13) > PhdFarZ ||
ABS(PhdMatrixPtr->_23) > PhdFarZ )
{
return FALSE;
}
return TRUE;
}
void __cdecl phd_TranslateAbs(int x, int y, int z) {
x -= MatrixW2V._03;
y -= MatrixW2V._13;
z -= MatrixW2V._23;
PhdMatrixPtr->_03 = x * PhdMatrixPtr->_00 + y * PhdMatrixPtr->_01 + z * PhdMatrixPtr->_02;
PhdMatrixPtr->_13 = x * PhdMatrixPtr->_10 + y * PhdMatrixPtr->_11 + z * PhdMatrixPtr->_12;
PhdMatrixPtr->_23 = x * PhdMatrixPtr->_20 + y * PhdMatrixPtr->_21 + z * PhdMatrixPtr->_22;
}
void __cdecl phd_PutPolygons(__int16 *ptrObj, int clip) {
FltWinLeft = (float)PhdWinMinX;
FltWinTop = (float)PhdWinMinY;
FltWinRight = (float)(PhdWinMinX + PhdWinMaxX + 1);
FltWinBottom = (float)(PhdWinMinY + PhdWinMaxY + 1);
FltWinCenterX = (float)(PhdWinMinX + PhdWinCenterX);
FltWinCenterY = (float)(PhdWinMinY + PhdWinCenterY);
#ifdef FEATURE_VIDEOFX_IMPROVED
__int16 *ptrEnv = ptrObj;
#endif // FEATURE_VIDEOFX_IMPROVED
ptrObj += 4; // skip x, y, z, radius
ptrObj = calc_object_vertices(ptrObj);
if( ptrObj != NULL ) {
ptrObj = calc_vertice_light(ptrObj);
ptrObj = ins_objectGT4(ptrObj+1, *ptrObj, ST_AvgZ);
ptrObj = ins_objectGT3(ptrObj+1, *ptrObj, ST_AvgZ);
ptrObj = ins_objectG4(ptrObj+1, *ptrObj, ST_AvgZ);
ptrObj = ins_objectG3(ptrObj+1, *ptrObj, ST_AvgZ);
#ifdef FEATURE_VIDEOFX_IMPROVED
phd_PutEnvmapPolygons(ptrEnv);
#endif // FEATURE_VIDEOFX_IMPROVED
}
}
void __cdecl S_InsertRoom(__int16 *ptrObj, BOOL isOutside) {
FltWinLeft = (float)(PhdWinMinX + PhdWinLeft);
FltWinTop = (float)(PhdWinMinY + PhdWinTop);
FltWinRight = (float)(PhdWinMinX + PhdWinRight + 1);
FltWinBottom = (float)(PhdWinMinY + PhdWinBottom + 1);
FltWinCenterX = (float)(PhdWinMinX + PhdWinCenterX);
FltWinCenterY = (float)(PhdWinMinY + PhdWinCenterY);
ptrObj = calc_roomvert(ptrObj, isOutside?0x00:0x10);
ptrObj = ins_objectGT4(ptrObj+1, *ptrObj, ST_MaxZ);
ptrObj = ins_objectGT3(ptrObj+1, *ptrObj, ST_MaxZ);
ptrObj = ins_room_sprite(ptrObj+1, *ptrObj);
}
__int16 *__cdecl calc_background_light(__int16 *ptrObj) {
int vtxCount = *ptrObj++;
if( vtxCount > 0 ) {
ptrObj += 3 * vtxCount;
}
else if( vtxCount < 0 ) {
vtxCount = -vtxCount;
ptrObj += vtxCount;
}
// Skybox has normal brightness
int shade = 0x0FFF;
// NOTE: Sunset did not change the skybox brightness in the original game
if( GF_SunsetEnabled )
shade += 0x400 * SunsetTimer / SUNSET_TIMEOUT;
for( int i = 0; i < vtxCount; ++i )
PhdVBuf[i].g = shade;
return ptrObj;
}
void __cdecl S_InsertBackground(__int16 *ptrObj) {
FltWinLeft = (float)(PhdWinMinX + PhdWinLeft);
FltWinTop = (float)(PhdWinMinY + PhdWinTop);
FltWinRight = (float)(PhdWinMinX + PhdWinRight + 1);
FltWinBottom = (float)(PhdWinMinY + PhdWinBottom + 1);
FltWinCenterX = (float)(PhdWinMinX + PhdWinCenterX);
FltWinCenterY = (float)(PhdWinMinY + PhdWinCenterY);
ptrObj += 4; // skip x, y, z, radius
ptrObj = calc_object_vertices(ptrObj);
if( ptrObj == NULL ) {
return;
}
ptrObj = calc_background_light(ptrObj);
#ifdef FEATURE_VIEW_IMPROVED
MidSort = 0xFFFF;
#endif // FEATURE_VIEW_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware ) {
HWR_EnableZBuffer(false, false);
}
ptrObj = ins_objectGT4(ptrObj+1, *ptrObj, ST_FarZ);
ptrObj = ins_objectGT3(ptrObj+1, *ptrObj, ST_FarZ);
ptrObj = ins_objectG4(ptrObj+1, *ptrObj, ST_FarZ);
ptrObj = ins_objectG3(ptrObj+1, *ptrObj, ST_FarZ);
if( SavedAppSettings.RenderMode == RM_Hardware ) {
HWR_EnableZBuffer(true, true);
}
#ifdef FEATURE_VIEW_IMPROVED
MidSort = 0;
#endif // FEATURE_VIEW_IMPROVED
}
void __cdecl S_InsertInvBgnd(__int16 *ptrObj) {
// NOTE: Null function in the PC version.
// But there is waving inventory function in the PlayStation version.
// Main S_InsertInvBgnd() logic is similar to S_InsertBackground();
}
__int16 *__cdecl calc_object_vertices(__int16 *ptrObj) {
double xv, yv, zv, persp, baseZ;
int vtxCount;
BYTE totalClip, clipFlags;
baseZ = 0.0;
#ifndef FEATURE_VIEW_IMPROVED
if( SavedAppSettings.RenderMode == RM_Software || !SavedAppSettings.ZBuffer ) {
baseZ = (double)(MidSort << (W2V_SHIFT + 8));
}
#endif // !FEATURE_VIEW_IMPROVED
totalClip = 0xFF;
ptrObj++; // skip poly counter
vtxCount = *(ptrObj++); // get vertex counter
if( vtxCount < 0 ) {
printf("vtxCount=%d", vtxCount);
}
for( int i = 0; i < vtxCount; ++i ) {
xv = (double)(PhdMatrixPtr->_00 * ptrObj[0] +
PhdMatrixPtr->_01 * ptrObj[1] +
PhdMatrixPtr->_02 * ptrObj[2] +
PhdMatrixPtr->_03);
yv = (double)(PhdMatrixPtr->_10 * ptrObj[0] +
PhdMatrixPtr->_11 * ptrObj[1] +
PhdMatrixPtr->_12 * ptrObj[2] +
PhdMatrixPtr->_13);
zv = (double)(PhdMatrixPtr->_20 * ptrObj[0] +
PhdMatrixPtr->_21 * ptrObj[1] +
PhdMatrixPtr->_22 * ptrObj[2] +
PhdMatrixPtr->_23);
PhdVBuf[i].xv = xv;
PhdVBuf[i].yv = yv;
if( zv < FltNearZ ) {
clipFlags = 0x80;
PhdVBuf[i].zv = zv;
} else {
clipFlags = 0;
if( zv >= FltFarZ ) {
zv = FltFarZ;
PhdVBuf[i].zv = zv;
} else {
PhdVBuf[i].zv = zv + baseZ;
}
persp = FltPersp / zv;
PhdVBuf[i].xs = persp * xv + FltWinCenterX;
PhdVBuf[i].ys = persp * yv + FltWinCenterY;
PhdVBuf[i].rhw = persp * FltRhwOPersp;
if( PhdVBuf[i].xs < FltWinLeft )
clipFlags |= 0x01;
else if( PhdVBuf[i].xs > FltWinRight )
clipFlags |= 0x02;
if( PhdVBuf[i].ys < FltWinTop )
clipFlags |= 0x04;
else if( PhdVBuf[i].ys > FltWinBottom )
clipFlags |= 0x08;
}
PhdVBuf[i].clip = clipFlags;
totalClip &= clipFlags;
ptrObj += 3;
}
return ( totalClip == 0 ) ? ptrObj : NULL;
}
__int16 *__cdecl calc_vertice_light(__int16 *ptrObj) {
int i, xv, yv, zv;
__int16 shade;
int vtxCount = *ptrObj++;
if( vtxCount > 0 ) {
if( LsDivider != 0 ) {
xv = (PhdMatrixPtr->_00 * LsVectorView.x +
PhdMatrixPtr->_10 * LsVectorView.y +
PhdMatrixPtr->_20 * LsVectorView.z) / LsDivider;
yv = (PhdMatrixPtr->_01 * LsVectorView.x +
PhdMatrixPtr->_11 * LsVectorView.y +
PhdMatrixPtr->_21 * LsVectorView.z) / LsDivider;
zv = (PhdMatrixPtr->_02 * LsVectorView.x +
PhdMatrixPtr->_12 * LsVectorView.y +
PhdMatrixPtr->_22 * LsVectorView.z) / LsDivider;
for( i = 0; i < vtxCount; ++i ) {
shade = LsAdder + ((ptrObj[0]*xv + ptrObj[1]*yv + ptrObj[2]*zv) >> 16);
CLAMP(shade, 0, 0x1FFF);
PhdVBuf[i].g = shade;
ptrObj += 3;
}
} else {
shade = LsAdder;
CLAMP(shade, 0, 0x1FFF);
for( i = 0; i < vtxCount; ++i ) {
PhdVBuf[i].g = shade;
}
ptrObj += 3*vtxCount;
}
} else {
for( i = 0; i < -vtxCount; ++i ) {
shade = LsAdder + *ptrObj;
CLAMP(shade, 0, 0x1FFF);
PhdVBuf[i].g = shade;
++ptrObj;
}
}
return ptrObj;
}
__int16 *__cdecl calc_roomvert(__int16 *ptrObj, BYTE farClip) {
double xv, yv, zv, persp, baseZ, depth;
int vtxCount, zv_int;
baseZ = 0.0;
#ifndef FEATURE_VIEW_IMPROVED
if( SavedAppSettings.RenderMode == RM_Software || !SavedAppSettings.ZBuffer ) {
baseZ = (double)(MidSort << (W2V_SHIFT + 8));
}
#endif // !FEATURE_VIEW_IMPROVED
vtxCount = *(ptrObj++);
for( int i = 0; i < vtxCount; ++i ) {
xv = (double)(PhdMatrixPtr->_00 * ptrObj[0] +
PhdMatrixPtr->_01 * ptrObj[1] +
PhdMatrixPtr->_02 * ptrObj[2] +
PhdMatrixPtr->_03);
yv = (double)(PhdMatrixPtr->_10 * ptrObj[0] +
PhdMatrixPtr->_11 * ptrObj[1] +
PhdMatrixPtr->_12 * ptrObj[2] +
PhdMatrixPtr->_13);
zv_int = (PhdMatrixPtr->_20 * ptrObj[0] +
PhdMatrixPtr->_21 * ptrObj[1] +
PhdMatrixPtr->_22 * ptrObj[2] +
PhdMatrixPtr->_23);
zv = (double)zv_int;
PhdVBuf[i].xv = xv;
PhdVBuf[i].yv = yv;
PhdVBuf[i].g = ptrObj[5];
if( IsWaterEffect != 0 )
PhdVBuf[i].g += ShadesTable[(WibbleOffset + (BYTE)RandomTable[(vtxCount - i) % WIBBLE_SIZE]) % WIBBLE_SIZE];
if( zv < FltNearZ ) {
PhdVBuf[i].clip = 0xFF80;
PhdVBuf[i].zv = zv;
} else {
persp = FltPersp / zv;
depth = zv_int >> W2V_SHIFT;
#ifdef FEATURE_VIEW_IMPROVED
if( depth >= PhdViewDistance ) {
PhdVBuf[i].rhw = persp * FltRhwOPersp;
PhdVBuf[i].zv = zv + baseZ;
#else // !FEATURE_VIEW_IMPROVED
if( depth >= DEPTHQ_END ) { // fog end
PhdVBuf[i].rhw = 0.0; // NOTE: zero RHW is an invalid value, but the original game sets it.
PhdVBuf[i].zv = FltFarZ;
#endif // FEATURE_VIEW_IMPROVED
PhdVBuf[i].g = 0x1FFF;
PhdVBuf[i].clip = farClip;
} else {
#ifdef FEATURE_VIEW_IMPROVED
PhdVBuf[i].g += CalculateFogShade(depth);
#else // !FEATURE_VIEW_IMPROVED
if( depth > DEPTHQ_START ) { // fog begin
PhdVBuf[i].g += depth - DEPTHQ_START;
}
#endif // FEATURE_VIEW_IMPROVED
PhdVBuf[i].rhw = persp * FltRhwOPersp;
PhdVBuf[i].clip = 0;
PhdVBuf[i].zv = zv + baseZ;
}
PhdVBuf[i].xs = persp * xv + FltWinCenterX;
PhdVBuf[i].ys = persp * yv + FltWinCenterY;
if( IsWibbleEffect && ptrObj[4] >= 0 ) {
PhdVBuf[i].xs += WibbleTable[(WibbleOffset + (BYTE)PhdVBuf[i].ys) % WIBBLE_SIZE];
PhdVBuf[i].ys += WibbleTable[(WibbleOffset + (BYTE)PhdVBuf[i].xs) % WIBBLE_SIZE];
}
if( PhdVBuf[i].xs < FltWinLeft )
PhdVBuf[i].clip |= 0x01;
else if( PhdVBuf[i].xs > FltWinRight )
PhdVBuf[i].clip |= 0x02;
if( PhdVBuf[i].ys < FltWinTop )
PhdVBuf[i].clip |= 0x04;
else if( PhdVBuf[i].ys > FltWinBottom )
PhdVBuf[i].clip |= 0x08;
PhdVBuf[i].clip |= ~(BYTE)(PhdVBuf[i].zv / 0x155555.p0) << 8;
}
CLAMP(PhdVBuf[i].g, 0, 0x1FFF);
ptrObj += 6;
}
return ptrObj;
}
void __cdecl phd_RotateLight(__int16 pitch, __int16 yaw) {
int xcos, ysin, wcos, wsin;
int ls_x, ls_y, ls_z;
PhdLsYaw = yaw;
PhdLsPitch = pitch;
xcos = phd_cos(pitch);
ysin = phd_sin(pitch);
wcos = phd_cos(yaw);
wsin = phd_sin(yaw);
ls_x = TRIGMULT2(xcos, wsin);
ls_y = -ysin;
ls_z = TRIGMULT2(xcos, wcos);
LsVectorView.x = (MatrixW2V._00 * ls_x + MatrixW2V._01 * ls_y + MatrixW2V._02 * ls_z) >> W2V_SHIFT;
LsVectorView.y = (MatrixW2V._10 * ls_x + MatrixW2V._11 * ls_y + MatrixW2V._12 * ls_z) >> W2V_SHIFT;
LsVectorView.z = (MatrixW2V._20 * ls_x + MatrixW2V._21 * ls_y + MatrixW2V._22 * ls_z) >> W2V_SHIFT;
}
void __cdecl phd_InitPolyList() {
SurfaceCount = 0;
Sort3dPtr = SortBuffer;
Info3dPtr = Info3dBuffer;
if( SavedAppSettings.RenderMode == RM_Hardware )
HWR_VertexPtr = HWR_VertexBuffer;
}
void __cdecl phd_SortPolyList() {
if( SurfaceCount ) {
for( DWORD i=0; i compare) ) ++i;
while( (left < j) && (compare > SortBuffer[j]._1) ) --j;
if( i > j ) break;
SWAP(SortBuffer[i]._0, SortBuffer[j]._0, swapBuf);
SWAP(SortBuffer[i]._1, SortBuffer[j]._1, swapBuf);
} while( ++i <= --j );
if( left < j )
do_quickysorty(left, j);
if( i < right )
do_quickysorty(i, right);
}
void __cdecl phd_PrintPolyList(BYTE *surfacePtr) {
__int16 polyType, *bufPtr;
PrintSurfacePtr = surfacePtr;
for( DWORD i=0; i_00 = W2V_SCALE;
PhdMatrixPtr->_11 = W2V_SCALE;
PhdMatrixPtr->_22 = W2V_SCALE;
}
/*
* Inject function
*/
void Inject_3Dgen() {
INJECT(0x00401000, phd_GenerateW2V);
INJECT(0x004011D0, phd_LookAt);
INJECT(0x00401250, phd_GetVectorAngles);
INJECT(0x004012D0, phd_RotX);
INJECT(0x00401380, phd_RotY);
INJECT(0x00401430, phd_RotZ);
INJECT(0x004014E0, phd_RotYXZ);
INJECT(0x004016C0, phd_RotYXZpack);
INJECT(0x004018B0, phd_TranslateRel);
INJECT(0x00401960, phd_TranslateAbs);
INJECT(0x004019E0, phd_PutPolygons);
INJECT(0x00401AE0, S_InsertRoom);
INJECT(0x00401BD0, calc_background_light);
INJECT(0x00401C10, S_InsertBackground);
// INJECT(----------, S_InsertInvBgnd); // NOTE: this is null in the original code
INJECT(0x00401D50, calc_object_vertices);
INJECT(0x00401F30, calc_vertice_light);
INJECT(0x004020A0, calc_roomvert);
INJECT(0x00402320, phd_RotateLight);
INJECT(0x004023F0, phd_InitPolyList);
INJECT(0x00402420, phd_SortPolyList);
INJECT(0x00402460, do_quickysorty);
INJECT(0x00402530, phd_PrintPolyList);
INJECT(0x00402570, AlterFOV);
INJECT(0x00402680, phd_SetNearZ);
INJECT(0x004026D0, phd_SetFarZ);
INJECT(0x004026F0, phd_InitWindow);
// INJECT(----------, phd_PopMatrix); // NOTE: this is inline or macro in the original code
INJECT(0x00457510, phd_PushMatrix);
INJECT(0x0045752E, phd_PushUnitMatrix);
}
================================================
FILE: 3dsystem/3d_gen.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef _3DGEN_H_INCLUDED
#define _3DGEN_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#ifdef FEATURE_VIDEOFX_IMPROVED
void ClearMeshReflectState();
void SetMeshReflectState(int objID, int meshIdx);
#endif // FEATURE_VIDEOFX_IMPROVED
void phd_GenerateW2V(PHD_3DPOS *viewPos); // 0x00401000
void __cdecl phd_LookAt(int xsrc, int ysrc, int zsrc, int xtar, int ytar, int ztar, __int16 roll); // 0x004011D0
void __cdecl phd_GetVectorAngles(int x, int y, int z, VECTOR_ANGLES *angles); // 0x00401250
void __cdecl phd_RotX(__int16 angle); // 0x004012D0
void __cdecl phd_RotY(__int16 angle); // 0x00401380
void __cdecl phd_RotZ(__int16 angle); // 0x00401430
void __cdecl phd_RotYXZ(__int16 ry, __int16 rx, __int16 rz); // 0x004014E0
void __cdecl phd_RotYXZpack(DWORD rpack); // 0x004016C0
BOOL __cdecl phd_TranslateRel(int x, int y, int z); // 0x004018B0
void __cdecl phd_TranslateAbs(int x, int y, int z); // 0x00401960
void __cdecl phd_PutPolygons(__int16 *ptrObj, int clip); // 0x004019E0
void __cdecl S_InsertRoom(__int16 *ptrObj, BOOL isOutside); // 0x00401AE0
__int16 *__cdecl calc_background_light(__int16 *ptrObj); // 0x00401BD0
void __cdecl S_InsertBackground(__int16 *ptrObj); // 0x00401C10
void __cdecl S_InsertInvBgnd(__int16 *ptrObj); // ----------
__int16 *__cdecl calc_object_vertices(__int16 *ptrObj); // 0x00401D50
__int16 *__cdecl calc_vertice_light(__int16 *ptrObj); // 0x00401F30
__int16 *__cdecl calc_roomvert(__int16 *ptrObj, BYTE farClip); // 0x004020A0
void __cdecl phd_RotateLight(__int16 pitch, __int16 yaw); // 0x00402320
void __cdecl phd_InitPolyList(); // 0x004023F0
void __cdecl phd_SortPolyList(); // 0x00402420
void __cdecl do_quickysorty(int left, int right); // 0x00402460
void __cdecl phd_PrintPolyList(BYTE *surfacePtr); // 0x00402530
void __cdecl AlterFOV(__int16 fov); // 0x00402570
void __cdecl phd_SetNearZ(int nearZ); // 0x00402680
void __cdecl phd_SetFarZ(int farZ); // 0x004026D0
void __cdecl phd_InitWindow(__int16 x, __int16 y, int width, int height, int nearZ, int farZ, __int16 viewAngle, int screenWidth, int screenHeight); // 0x004026F0
void __cdecl phd_PopMatrix(); // ----------
void __cdecl phd_PushMatrix(); // 0x00457510
void __cdecl phd_PushUnitMatrix(); // 0x0045752E
#endif // _3DGEN_H_INCLUDED
================================================
FILE: 3dsystem/3d_out.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "3dsystem/3d_out.h"
#include "global/vars.h"
#pragma pack(push, 1)
typedef struct {
UINT16 x;
UINT16 y;
} XGEN_X;
typedef struct {
UINT16 x;
UINT16 y;
UINT16 g;
} XGEN_XG;
typedef struct {
UINT16 x;
UINT16 y;
UINT16 g;
UINT16 u;
UINT16 v;
} XGEN_XGUV;
typedef struct {
UINT16 x;
UINT16 y;
UINT16 g;
float rhw;
float u;
float v;
} XGEN_XGUVP;
typedef struct {
int x0;
int x1;
} XBUF_X;
typedef struct {
int x0;
int g0;
int x1;
int g1;
} XBUF_XG;
typedef struct {
int x0;
int g0;
int u0;
int v0;
int x1;
int g1;
int u1;
int v1;
} XBUF_XGUV;
typedef struct {
int x0;
int g0;
float u0;
float v0;
float rhw0;
int x1;
int g1;
float u1;
float v1;
float rhw1;
} XBUF_XGUVP;
#pragma pack(pop)
#ifdef FEATURE_NOLEGACY_OPTIONS
static int SwrPitch = 0;
static int SwrHeight = 0;
static void *XBuffer = NULL;
int GetPitchSWR() {
return SwrPitch;
}
void PrepareSWR(int pitch, int height) {
if( pitch != 0 ) {
SwrPitch = pitch;
}
if( height != 0 && (XBuffer == NULL || SwrHeight != height) ) {
SwrHeight = height;
if( XBuffer != NULL ) free(XBuffer);
XBuffer = malloc(sizeof(XBUF_XGUVP) * height);
}
}
#else // FEATURE_NOLEGACY_OPTIONS
#define SwrPitch PhdScreenWidth // NOTE: this is the original game bug!
static int XBuffer[1200 * sizeof(XBUF_XGUVP) / sizeof(int)]; // maximum safe resolution is 1200 pixels
#endif // FEATURE_NOLEGACY_OPTIONS
void __cdecl draw_poly_line(__int16 *bufPtr) {
int i, j;
int x0, y0, x1, y1;
int xSize, ySize, xAdd, yAdd, colAdd, rowAdd;
int swapBuf, part, partTotal;
BYTE colorIdx;
BYTE *drawPtr;
x0 = *(bufPtr++);
y0 = *(bufPtr++);
x1 = *(bufPtr++);
y1 = *(bufPtr++);
colorIdx = (BYTE)*bufPtr;
if( x1 < x0 ) {
SWAP(x0, x1, swapBuf);
SWAP(y0, y1, swapBuf);
}
if( x1 < 0 || x0 > PhdWinMaxX )
return;
if( x0 < 0 ) {
y0 -= x0 * (y1 - y0) / (x1 - x0);
x0 = 0;
}
if( x1 > PhdWinMaxX ) {
y1 = y0 + (y1 - y0) * (PhdWinMaxX - x0) / (x1 - x0);
x1 = PhdWinMaxX;
}
if( y1 < y0 ) {
SWAP(x0, x1, swapBuf);
SWAP(y0, y1, swapBuf);
}
if( y1 < 0 || y0 > PhdWinMaxY )
return;
if( y0 < 0 ) {
x0 -= y0 * (x1 - x0) / (y1 - y0);
y0 = 0;
}
if( y1 > PhdWinMaxY ) {
x1 = x0 + (x1 - x0) * (PhdWinMaxY - y0) / (y1 - y0);
y1 = PhdWinMaxY;
}
drawPtr = PrintSurfacePtr + (SwrPitch * y0 + x0);
xSize = x1 - x0;
ySize = y1 - y0;
if( (xSize|ySize) == 0 ) {
*drawPtr = colorIdx;
return;
}
if( xSize < 0 ) {
xSize = -xSize;
xAdd = -1;
} else {
xAdd = 1;
}
if( ySize < 0 ) {
ySize = -ySize;
yAdd = -SwrPitch;
} else {
yAdd = SwrPitch;
}
if( xSize >= ySize ) {
i = xSize + 1;
j = ySize + 1;
colAdd = xAdd;
rowAdd = yAdd;
} else {
i = ySize + 1;
j = xSize + 1;
colAdd = yAdd;
rowAdd = xAdd;
}
partTotal = 0;
part = PHD_ONE * j / i;
while( i-- ) {
partTotal += part;
*drawPtr = colorIdx;
drawPtr += colAdd;
if( partTotal >= PHD_ONE ) {
drawPtr += rowAdd;
partTotal -= PHD_ONE;
}
}
}
void __cdecl draw_poly_flat(__int16 *bufPtr) {
if( xgen_x(bufPtr + 1) )
flatA(XGen_y0, XGen_y1, *bufPtr);
}
void __cdecl draw_poly_trans(__int16 *bufPtr) {
if( xgen_x(bufPtr + 1) )
transA(XGen_y0, XGen_y1, *bufPtr);
}
void __cdecl draw_poly_gouraud(__int16 *bufPtr) {
if( xgen_xg(bufPtr + 1) )
gourA(XGen_y0, XGen_y1, *bufPtr);
}
void __cdecl draw_poly_gtmap(__int16 *bufPtr) {
if( xgen_xguv(bufPtr + 1) )
gtmapA(XGen_y0, XGen_y1, TexturePageBuffer8[*bufPtr]);
}
void __cdecl draw_poly_wgtmap(__int16 *bufPtr) {
if( xgen_xguv(bufPtr + 1) )
wgtmapA(XGen_y0, XGen_y1, TexturePageBuffer8[*bufPtr]);
}
BOOL __cdecl xgen_x(__int16 *bufPtr) {
int ptCount;
XGEN_X *pt1, *pt2;
int yMin, yMax;
int x1, y1, x2, y2;
int xSize, ySize;
int x, xAdd;
XBUF_X *xPtr;
ptCount = *bufPtr++;
pt2 = (XGEN_X *)bufPtr;
pt1 = pt2 + (ptCount - 1);
yMin = yMax = pt1->y;
while( ptCount-- ) {
x1 = pt1->x;
y1 = pt1->y;
x2 = pt2->x;
y2 = pt2->y;
pt1 = pt2++;
if( y1 < y2 ) {
CLAMPG(yMin, y1);
xSize = x2 - x1;
ySize = y2 - y1;
xPtr = (XBUF_X *)XBuffer + y1;
xAdd = PHD_ONE * xSize / ySize;
x = x1 * PHD_ONE + (PHD_ONE - 1);
do {
(xPtr++)->x1 = (x += xAdd);
} while( --ySize );
}
else if( y2 < y1 ) {
CLAMPL(yMax, y1);
xSize = x1 - x2;
ySize = y1 - y2;
xPtr = (XBUF_X *)XBuffer + y2;
xAdd = PHD_ONE * xSize / ySize;
x = x2 * PHD_ONE + 1;
do {
(xPtr++)->x0 = (x += xAdd);
} while( --ySize );
}
}
if( yMin == yMax )
return FALSE;
XGen_y0 = yMin;
XGen_y1 = yMax;
return TRUE;
}
BOOL __cdecl xgen_xg(__int16 *bufPtr) {
int ptCount;
XGEN_XG *pt1, *pt2;
int yMin, yMax;
int x1, y1, g1, x2, y2, g2;
int xSize, ySize, gSize;
int x, g, xAdd, gAdd;
XBUF_XG *xgPtr;
ptCount = *bufPtr++;
pt2 = (XGEN_XG *)bufPtr;
pt1 = pt2 + (ptCount - 1);
yMin = yMax = pt1->y;
while( ptCount-- ) {
x1 = pt1->x;
y1 = pt1->y;
g1 = pt1->g;
x2 = pt2->x;
y2 = pt2->y;
g2 = pt2->g;
pt1 = pt2++;
if( y1 < y2 ) {
CLAMPG(yMin, y1);
xSize = x2 - x1;
ySize = y2 - y1;
gSize = g2 - g1;
xgPtr = (XBUF_XG *)XBuffer + y1;
xAdd = PHD_ONE * xSize / ySize;
gAdd = PHD_HALF * gSize / ySize;
x = x1 * PHD_ONE + (PHD_ONE - 1);
g = g1 * PHD_HALF;
do {
xgPtr->x1 = (x += xAdd);
xgPtr->g1 = (g += gAdd);
xgPtr++;
} while( --ySize );
}
else if( y2 < y1 ) {
CLAMPL(yMax, y1);
xSize = x1 - x2;
ySize = y1 - y2;
gSize = g1 - g2;
xgPtr = (XBUF_XG *)XBuffer + y2;
xAdd = PHD_ONE * xSize / ySize;
gAdd = PHD_HALF * gSize / ySize;
x = x2 * PHD_ONE + 1;
g = g2 * PHD_HALF;
do {
xgPtr->x0 = (x += xAdd);
xgPtr->g0 = (g += gAdd);
xgPtr++;
} while( --ySize );
}
}
if( yMin == yMax )
return FALSE;
XGen_y0 = yMin;
XGen_y1 = yMax;
return TRUE;
}
BOOL __cdecl xgen_xguv(__int16 *bufPtr) {
int ptCount;
XGEN_XGUV *pt1, *pt2;
int yMin, yMax;
int x1, y1, g1, u1, v1, x2, y2, g2, u2, v2;
int xSize, ySize, gSize, uSize, vSize;
int x, g, u, v, xAdd, gAdd, uAdd, vAdd;
XBUF_XGUV *xguvPtr;
ptCount = *bufPtr++;
pt2 = (XGEN_XGUV *)bufPtr;
pt1 = pt2 + (ptCount - 1);
yMin = yMax = pt1->y;
while( ptCount-- ) {
x1 = pt1->x;
y1 = pt1->y;
g1 = pt1->g;
u1 = pt1->u;
v1 = pt1->v;
x2 = pt2->x;
y2 = pt2->y;
g2 = pt2->g;
u2 = pt2->u;
v2 = pt2->v;
pt1 = pt2++;
if( y1 < y2 ) {
CLAMPG(yMin, y1);
xSize = x2 - x1;
ySize = y2 - y1;
gSize = g2 - g1;
uSize = u2 - u1;
vSize = v2 - v1;
xguvPtr = (XBUF_XGUV *)XBuffer + y1;
xAdd = PHD_ONE * xSize / ySize;
gAdd = PHD_HALF * gSize / ySize;
uAdd = PHD_HALF * uSize / ySize;
vAdd = PHD_HALF * vSize / ySize;
x = x1 * PHD_ONE + (PHD_ONE - 1);
g = g1 * PHD_HALF;
u = u1 * PHD_HALF;
v = v1 * PHD_HALF;
do {
xguvPtr->x1 = (x += xAdd);
xguvPtr->g1 = (g += gAdd);
xguvPtr->u1 = (u += uAdd);
xguvPtr->v1 = (v += vAdd);
xguvPtr++;
} while( --ySize );
}
else if( y2 < y1 ) {
CLAMPL(yMax, y1);
xSize = x1 - x2;
ySize = y1 - y2;
gSize = g1 - g2;
uSize = u1 - u2;
vSize = v1 - v2;
xguvPtr = (XBUF_XGUV *)XBuffer + y2;
xAdd = PHD_ONE * xSize / ySize;
gAdd = PHD_HALF * gSize / ySize;
uAdd = PHD_HALF * uSize / ySize;
vAdd = PHD_HALF * vSize / ySize;
x = x2 * PHD_ONE + 1;
g = g2 * PHD_HALF;
u = u2 * PHD_HALF;
v = v2 * PHD_HALF;
do {
xguvPtr->x0 = (x += xAdd);
xguvPtr->g0 = (g += gAdd);
xguvPtr->u0 = (u += uAdd);
xguvPtr->v0 = (v += vAdd);
xguvPtr++;
} while( --ySize );
}
}
if( yMin == yMax )
return FALSE;
XGen_y0 = yMin;
XGen_y1 = yMax;
return TRUE;
}
BOOL __cdecl xgen_xguvpersp_fp(__int16 *bufPtr) {
int ptCount;
XGEN_XGUVP *pt1, *pt2;
int yMin, yMax;
int x1, y1, g1, x2, y2, g2;
float u1, v1, rhw1, u2, v2, rhw2;
int xSize, ySize, gSize;
float uSize, vSize, rhwSize;
int x, g, xAdd, gAdd;
float u, v, rhw, uAdd, vAdd, rhwAdd;
XBUF_XGUVP *xguvPtr;
ptCount = *bufPtr++;
pt2 = (XGEN_XGUVP *)bufPtr;
pt1 = pt2 + (ptCount - 1);
yMin = yMax = pt1->y;
while( ptCount-- ) {
x1 = pt1->x;
y1 = pt1->y;
g1 = pt1->g;
u1 = pt1->u;
v1 = pt1->v;
rhw1 = pt1->rhw;
x2 = pt2->x;
y2 = pt2->y;
g2 = pt2->g;
u2 = pt2->u;
v2 = pt2->v;
rhw2 = pt2->rhw;
pt1 = pt2++;
if( y1 < y2 ) {
CLAMPG(yMin, y1);
xSize = x2 - x1;
ySize = y2 - y1;
gSize = g2 - g1;
uSize = u2 - u1;
vSize = v2 - v1;
rhwSize = rhw2 - rhw1;
xguvPtr = (XBUF_XGUVP *)XBuffer + y1;
xAdd = PHD_ONE * xSize / ySize;
gAdd = PHD_HALF * gSize / ySize;
uAdd = uSize / (float)ySize;
vAdd = vSize / (float)ySize;
rhwAdd = rhwSize / (float)ySize;
x = x1 * PHD_ONE + (PHD_ONE - 1);
g = g1 * PHD_HALF;
u = u1;
v = v1;
rhw = rhw1;
do {
xguvPtr->x1 = (x += xAdd);
xguvPtr->g1 = (g += gAdd);
xguvPtr->u1 = (u += uAdd);
xguvPtr->v1 = (v += vAdd);
xguvPtr->rhw1 = (rhw += rhwAdd);
xguvPtr++;
} while( --ySize );
}
else if( y2 < y1 ) {
CLAMPL(yMax, y1);
xSize = x1 - x2;
ySize = y1 - y2;
gSize = g1 - g2;
uSize = u1 - u2;
vSize = v1 - v2;
rhwSize = rhw1 - rhw2;
xguvPtr = (XBUF_XGUVP *)XBuffer + y2;
xAdd = PHD_ONE * xSize / ySize;
gAdd = PHD_HALF * gSize / ySize;
uAdd = (float)uSize / (float)ySize;
vAdd = (float)vSize / (float)ySize;
rhwAdd = (float)rhwSize / (float)ySize;
x = x2 * PHD_ONE + 1;
g = g2 * PHD_HALF;
u = (float)u2;
v = (float)v2;
rhw = (float)rhw2;
do {
xguvPtr->x0 = (x += xAdd);
xguvPtr->g0 = (g += gAdd);
xguvPtr->u0 = (u += uAdd);
xguvPtr->v0 = (v += vAdd);
xguvPtr->rhw0 = (rhw += rhwAdd);
xguvPtr++;
} while( --ySize );
}
}
if( yMin == yMax )
return FALSE;
XGen_y0 = yMin;
XGen_y1 = yMax;
return TRUE;
}
void __cdecl gtmap_persp32_fp(int y0, int y1, BYTE *texPage) {
int batchSize, batchCounter;
int x, xSize, ySize;
int g, u0, u1, v0, v1, gAdd, u0Add, v0Add;
double u, v, rhw, uAdd, vAdd, rhwAdd;
BYTE *drawPtr, *linePtr;
XBUF_XGUVP *xbuf;
BYTE colorIdx;
ySize = y1 - y0;
if( ySize <= 0 )
return;
xbuf = (XBUF_XGUVP *)XBuffer + y0;
drawPtr = PrintSurfacePtr + y0 * SwrPitch;
for( ; ySize > 0; --ySize, ++xbuf, drawPtr += SwrPitch ) {
x = xbuf->x0 / PHD_ONE;
xSize = (xbuf->x1 / PHD_ONE) - x;
if( xSize <= 0 )
continue;
g = xbuf->g0;
u = xbuf->u0;
v = xbuf->v0;
rhw = xbuf->rhw0;
gAdd = (xbuf->g1 - g) / xSize;
u0 = (int)(PHD_HALF * u / rhw);
v0 = (int)(PHD_HALF * v / rhw);
linePtr = drawPtr + x;
batchSize = 32;
if( xSize >= batchSize ) {
uAdd = (xbuf->u1 - u) / (double)xSize * double(batchSize);
vAdd = (xbuf->v1 - v) / (double)xSize * double(batchSize);
rhwAdd = (xbuf->rhw1 - rhw) / (double)xSize * double(batchSize);
do {
u += uAdd;
v += vAdd;
rhw += rhwAdd;
u1 = (int)(PHD_HALF * u / rhw);
v1 = (int)(PHD_HALF * v / rhw);
u0Add = (u1 - u0) / batchSize;
v0Add = (v1 - v0) / batchSize;
if( (ABS(u0Add) + ABS(v0Add)) < (PHD_ONE / 2) ) {
batchCounter = batchSize / 2;
do {
colorIdx = texPage[BYTE2(v0)*256 + BYTE2(u0)];
colorIdx = DepthQTable[BYTE2(g)].index[colorIdx];
*(linePtr++) = colorIdx;
*(linePtr++) = colorIdx;
g += gAdd * 2;
u0 += u0Add * 2;
v0 += v0Add * 2;
} while( --batchCounter );
} else {
batchCounter = batchSize;
do {
colorIdx = texPage[BYTE2(v0)*256 + BYTE2(u0)];
*(linePtr++) = DepthQTable[BYTE2(g)].index[colorIdx];
g += gAdd;
u0 += u0Add;
v0 += v0Add;
} while( --batchCounter );
}
u0 = u1;
v0 = v1;
xSize -= batchSize;
} while( xSize >= batchSize );
}
if( xSize > 1 ) {
u1 = (int)(PHD_HALF * xbuf->u1 / xbuf->rhw1);
v1 = (int)(PHD_HALF * xbuf->v1 / xbuf->rhw1);
u0Add = (u1 - u0) / xSize;
v0Add = (v1 - v0) / xSize;
batchSize = xSize & ~1;
xSize -= batchSize;
if( (ABS(u0Add) + ABS(v0Add)) < (PHD_ONE / 2) ) {
batchCounter = batchSize / 2;
do {
colorIdx = texPage[BYTE2(v0)*256 + BYTE2(u0)];
colorIdx = DepthQTable[BYTE2(g)].index[colorIdx];
*(linePtr++) = colorIdx;
*(linePtr++) = colorIdx;
g += gAdd * 2;
u0 += u0Add * 2;
v0 += v0Add * 2;
} while( --batchCounter );
} else {
batchCounter = batchSize;
do {
colorIdx = texPage[BYTE2(v0)*256 + BYTE2(u0)];
*(linePtr++) = DepthQTable[BYTE2(g)].index[colorIdx];
g += gAdd;
u0 += u0Add;
v0 += v0Add;
} while( --batchCounter );
}
}
if( xSize != 0 ) { // xSize == 1
colorIdx = texPage[BYTE2(v0)*256 + BYTE2(u0)];
*linePtr = DepthQTable[BYTE2(g)].index[colorIdx];
}
}
}
void __cdecl wgtmap_persp32_fp(int y0, int y1, BYTE *texPage) {
int batchSize, batchCounter;
int x, xSize, ySize;
int g, u0, u1, v0, v1, gAdd, u0Add, v0Add;
double u, v, rhw, uAdd, vAdd, rhwAdd;
BYTE *drawPtr, *linePtr;
XBUF_XGUVP *xbuf;
BYTE colorIdx;
ySize = y1 - y0;
if( ySize <= 0 )
return;
xbuf = (XBUF_XGUVP *)XBuffer + y0;
drawPtr = PrintSurfacePtr + y0 * SwrPitch;
for( ; ySize > 0; --ySize, ++xbuf, drawPtr += SwrPitch ) {
x = xbuf->x0 / PHD_ONE;
xSize = (xbuf->x1 / PHD_ONE) - x;
if( xSize <= 0 )
continue;
g = xbuf->g0;
u = xbuf->u0;
v = xbuf->v0;
rhw = xbuf->rhw0;
gAdd = (xbuf->g1 - g) / xSize;
u0 = (int)(PHD_HALF * u / rhw);
v0 = (int)(PHD_HALF * v / rhw);
linePtr = drawPtr + x;
batchSize = 32;
if( xSize >= batchSize ) {
uAdd = (xbuf->u1 - u) / (double)xSize * double(batchSize);
vAdd = (xbuf->v1 - v) / (double)xSize * double(batchSize);
rhwAdd = (xbuf->rhw1 - rhw) / (double)xSize * double(batchSize);
do {
u += uAdd;
v += vAdd;
rhw += rhwAdd;
u1 = (int)(PHD_HALF * u / rhw);
v1 = (int)(PHD_HALF * v / rhw);
u0Add = (u1 - u0) / batchSize;
v0Add = (v1 - v0) / batchSize;
if( (ABS(u0Add) + ABS(v0Add)) < (PHD_ONE / 2) ) {
batchCounter = batchSize / 2;
do {
colorIdx = texPage[BYTE2(v0)*256 + BYTE2(u0)];
if( colorIdx != 0 ) {
colorIdx = DepthQTable[BYTE2(g)].index[colorIdx];
linePtr[0] = colorIdx;
linePtr[1] = colorIdx;
}
linePtr += 2;
g += gAdd * 2;
u0 += u0Add * 2;
v0 += v0Add * 2;
} while( --batchCounter );
} else {
batchCounter = batchSize;
do {
colorIdx = texPage[BYTE2(v0)*256 + BYTE2(u0)];
if( colorIdx != 0 ) {
*linePtr = DepthQTable[BYTE2(g)].index[colorIdx];
}
linePtr++;
g += gAdd;
u0 += u0Add;
v0 += v0Add;
} while( --batchCounter );
}
u0 = u1;
v0 = v1;
xSize -= batchSize;
} while( xSize >= batchSize );
}
if( xSize > 1 ) {
u1 = (int)(PHD_HALF * xbuf->u1 / xbuf->rhw1);
v1 = (int)(PHD_HALF * xbuf->v1 / xbuf->rhw1);
u0Add = (u1 - u0) / xSize;
v0Add = (v1 - v0) / xSize;
batchSize = xSize & ~1;
xSize -= batchSize;
if( (ABS(u0Add) + ABS(v0Add)) < (PHD_ONE / 2) ) {
batchCounter = batchSize / 2;
do {
colorIdx = texPage[BYTE2(v0)*256 + BYTE2(u0)];
if( colorIdx != 0 ) {
colorIdx = DepthQTable[BYTE2(g)].index[colorIdx];
linePtr[0] = colorIdx;
linePtr[1] = colorIdx;
}
linePtr += 2;
g += gAdd * 2;
u0 += u0Add * 2;
v0 += v0Add * 2;
} while( --batchCounter );
} else {
batchCounter = batchSize;
do {
colorIdx = texPage[BYTE2(v0)*256 + BYTE2(u0)];
if( colorIdx != 0 ) {
*linePtr = DepthQTable[BYTE2(g)].index[colorIdx];
}
linePtr++;
g += gAdd;
u0 += u0Add;
v0 += v0Add;
} while( --batchCounter );
}
}
if( xSize != 0 ) { // xSize == 1
colorIdx = texPage[BYTE2(v0)*256 + BYTE2(u0)];
if( colorIdx != 0 ) {
*linePtr = DepthQTable[BYTE2(g)].index[colorIdx];
}
}
}
}
void __cdecl draw_poly_gtmap_persp(__int16 *bufPtr) {
if( xgen_xguvpersp_fp(bufPtr + 1) )
gtmap_persp32_fp(XGen_y0, XGen_y1, TexturePageBuffer8[*bufPtr]);
}
void __cdecl draw_poly_wgtmap_persp(__int16 *bufPtr) {
if( xgen_xguvpersp_fp(bufPtr + 1) )
wgtmap_persp32_fp(XGen_y0, XGen_y1, TexturePageBuffer8[*bufPtr]);
}
void __fastcall flatA(int y0, int y1, BYTE colorIdx) {
int x, xSize, ySize;
BYTE *drawPtr;
XBUF_X *xbuf;
ySize = y1 - y0;
if( ySize <= 0 )
return;
xbuf = (XBUF_X *)XBuffer + y0;
drawPtr = PrintSurfacePtr + y0 * SwrPitch;
for( ; ySize > 0; --ySize, ++xbuf, drawPtr += SwrPitch ) {
x = xbuf->x0 / PHD_ONE;
xSize = (xbuf->x1 / PHD_ONE) - x;
if( xSize > 0 ) {
memset(drawPtr + x, colorIdx, xSize);
}
}
}
void __fastcall transA(int y0, int y1, BYTE depthQ) {
int x, xSize, ySize;
BYTE *drawPtr, *linePtr;
XBUF_X *xbuf;
DEPTHQ_ENTRY *qt;
ySize = y1 - y0;
if( ySize <= 0 || depthQ >= 32 ) // NOTE: depthQ check was ( > 32) in the original code
return;
xbuf = (XBUF_X *)XBuffer + y0;
drawPtr = PrintSurfacePtr + y0 * SwrPitch;
qt = DepthQTable + depthQ;
for( ; ySize > 0; --ySize, ++xbuf, drawPtr += SwrPitch ) {
x = xbuf->x0 / PHD_ONE;
xSize = (xbuf->x1 / PHD_ONE) - x;
if( xSize <= 0 )
continue;
linePtr = drawPtr + x;
do {
*linePtr = qt->index[*linePtr];
++linePtr;
} while( --xSize );
}
}
void __fastcall gourA(int y0, int y1, BYTE colorIdx) {
int x, xSize, ySize;
int g, gAdd;
BYTE *drawPtr, *linePtr;
XBUF_XG *xbuf;
GOURAUD_ENTRY *gt;
ySize = y1 - y0;
if( ySize <= 0 )
return;
xbuf = (XBUF_XG *)XBuffer + y0;
drawPtr = PrintSurfacePtr + y0 * SwrPitch;
gt = GouraudTable + colorIdx;
for( ; ySize > 0; --ySize, ++xbuf, drawPtr += SwrPitch ) {
x = xbuf->x0 / PHD_ONE;
xSize = (xbuf->x1 / PHD_ONE) - x;
if( xSize <= 0 )
continue;
g = xbuf->g0;
gAdd = (xbuf->g1 - g) / xSize;
linePtr = drawPtr + x;
do {
*(linePtr++) = gt->index[BYTE2(g)];
g += gAdd;
} while( --xSize );
}
}
void __fastcall gtmapA(int y0, int y1, BYTE *texPage) {
int x, xSize, ySize;
int g, u, v, gAdd, uAdd, vAdd;
BYTE *drawPtr, *linePtr;
XBUF_XGUV *xbuf;
BYTE colorIdx;
ySize = y1 - y0;
if( ySize <= 0 )
return;
xbuf = (XBUF_XGUV *)XBuffer + y0;
drawPtr = PrintSurfacePtr + y0 * SwrPitch;
for( ; ySize > 0; --ySize, ++xbuf, drawPtr += SwrPitch ) {
x = xbuf->x0 / PHD_ONE;
xSize = (xbuf->x1 / PHD_ONE) - x;
if( xSize <= 0 )
continue;
g = xbuf->g0;
u = xbuf->u0;
v = xbuf->v0;
gAdd = (xbuf->g1 - g) / xSize;
uAdd = (xbuf->u1 - u) / xSize;
vAdd = (xbuf->v1 - v) / xSize;
linePtr = drawPtr + x;
do {
colorIdx = texPage[BYTE2(v)*256 + BYTE2(u)];
*(linePtr++) = DepthQTable[BYTE2(g)].index[colorIdx];
g += gAdd;
u += uAdd;
v += vAdd;
} while( --xSize );
}
}
void __fastcall wgtmapA(int y0, int y1, BYTE *texPage) {
int x, xSize, ySize;
int g, u, v, gAdd, uAdd, vAdd;
BYTE *drawPtr, *linePtr;
XBUF_XGUV *xbuf;
BYTE colorIdx;
ySize = y1 - y0;
if( ySize <= 0 )
return;
xbuf = (XBUF_XGUV *)XBuffer + y0;
drawPtr = PrintSurfacePtr + y0 * SwrPitch;
for( ; ySize > 0; --ySize, ++xbuf, drawPtr += SwrPitch ) {
x = xbuf->x0 / PHD_ONE;
xSize = (xbuf->x1 / PHD_ONE) - x;
if( xSize <= 0 )
continue;
g = xbuf->g0;
u = xbuf->u0;
v = xbuf->v0;
gAdd = (xbuf->g1 - g) / xSize;
uAdd = (xbuf->u1 - u) / xSize;
vAdd = (xbuf->v1 - v) / xSize;
linePtr = drawPtr + x;
do {
colorIdx = texPage[BYTE2(v)*256 + BYTE2(u)];
if( colorIdx != 0 ) {
*linePtr = DepthQTable[BYTE2(g)].index[colorIdx];
}
++linePtr;
g += gAdd;
u += uAdd;
v += vAdd;
} while( --xSize );
}
}
/*
* Inject function
*/
void Inject_3Dout() {
INJECT(0x00402960, draw_poly_line);
INJECT(0x00402B00, draw_poly_flat);
INJECT(0x00402B40, draw_poly_trans);
INJECT(0x00402B80, draw_poly_gouraud);
INJECT(0x00402BC0, draw_poly_gtmap);
INJECT(0x00402C00, draw_poly_wgtmap);
INJECT(0x00402C40, xgen_x);
INJECT(0x00402D20, xgen_xg);
INJECT(0x00402E70, xgen_xguv);
INJECT(0x00403090, xgen_xguvpersp_fp);
INJECT(0x00403320, gtmap_persp32_fp);
INJECT(0x004042F0, wgtmap_persp32_fp);
INJECT(0x004057C0, draw_poly_gtmap_persp);
INJECT(0x00405800, draw_poly_wgtmap_persp);
// NOTE: asm functions below use Watcom register calling convention so they incompatible
// INJECT(0x00457564, flatA);
// INJECT(0x004575C5, transA);
// INJECT(0x004576FF, gourA);
// INJECT(0x0045785F, gtmapA);
// INJECT(0x00457B5C, wgtmapA);
}
================================================
FILE: 3dsystem/3d_out.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef _3DOUT_H_INCLUDED
#define _3DOUT_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl draw_poly_line(__int16 *bufPtr); // 0x00402960
void __cdecl draw_poly_flat(__int16 *bufPtr); // 0x00402B00
void __cdecl draw_poly_trans(__int16 *bufPtr); // 0x00402B40
void __cdecl draw_poly_gouraud(__int16 *bufPtr); // 0x00402B80
void __cdecl draw_poly_gtmap(__int16 *bufPtr); // 0x00402BC0
void __cdecl draw_poly_wgtmap(__int16 *bufPtr); // 0x00402C00
BOOL __cdecl xgen_x(__int16 *bufPtr); // 0x00402C40
BOOL __cdecl xgen_xg(__int16 *bufPtr); // 0x00402D20
BOOL __cdecl xgen_xguv(__int16 *bufPtr); // 0x00402E70
BOOL __cdecl xgen_xguvpersp_fp(__int16 *bufPtr); // 0x00403090
void __cdecl gtmap_persp32_fp(int y0, int y1, BYTE *texPage); // 0x00403320
void __cdecl wgtmap_persp32_fp(int y0, int y1, BYTE *texPage); // 0x004042F0
void __cdecl draw_poly_gtmap_persp(__int16 *bufPtr); // 0x004057C0
void __cdecl draw_poly_wgtmap_persp(__int16 *bufPtr); // 0x00405800
void __fastcall flatA(int y0, int y1, BYTE colorIdx); // 0x00457564
void __fastcall transA(int y0, int y1, BYTE depthQ); // 0x004575C5
void __fastcall gourA(int y0, int y1, BYTE colorIdx); // 0x004576FF
void __fastcall gtmapA(int y0, int y1, BYTE *texPage); // 0x0045785F
void __fastcall wgtmapA(int y0, int y1, BYTE *texPage); // 0x00457B5C
#endif // _3DOUT_H_INCLUDED
================================================
FILE: 3dsystem/3dinsert.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "3dsystem/3dinsert.h"
#include "specific/hwr.h"
#include "global/vars.h"
#if defined(FEATURE_HUD_IMPROVED) || (DIRECT3D_VERSION >= 0x900)
#include "modding/texture_utils.h"
#endif // defined(FEATURE_HUD_IMPROVED) || (DIRECT3D_VERSION >= 0x900)
#ifdef FEATURE_VIDEOFX_IMPROVED
#include "specific/texture.h"
#include "modding/mod_utils.h"
extern DWORD ShadowMode;
extern DWORD AlphaBlendMode;
bool CustomWaterColorEnabled = true;
#endif // FEATURE_VIDEOFX_IMPROVED
static VERTEX_INFO VBuffer[40]; // NOTE: original size was 20
static D3DTLVERTEX VBufferD3D[32];
static D3DCOLOR GlobalTint = 0; // NOTE: not presented in the original code
#ifdef FEATURE_VIEW_IMPROVED
bool RoomSortEnabled = false;
#define MAKE_ZSORT(z) (RoomSortEnabled && (SavedAppSettings.RenderMode == RM_Software || !SavedAppSettings.ZBuffer) ? (((UINT64)MidSort)<<32)+(DWORD)(z) : (DWORD)(z))
#else // FEATURE_VIEW_IMPROVED
#define MAKE_ZSORT(z) ((DWORD)(z))
#endif // FEATURE_VIEW_IMPROVED
static D3DCOLOR shadeColor(DWORD red, DWORD green, DWORD blue, DWORD alpha, DWORD shade, bool isTextured) {
CLAMPG(shade, 0x1FFF);
if( GlobalTint ) {
red = RGBA_GETRED(GlobalTint);
green = RGBA_GETGREEN(GlobalTint);
blue = RGBA_GETBLUE(GlobalTint);
alpha = RGBA_GETALPHA(GlobalTint);
}
#if defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.LightingMode == 1 ) CLAMPL(shade, 0x800);
if( SavedAppSettings.LightingMode && isTextured ) shade = 0x1000 + shade/2;
if( !SavedAppSettings.LightingMode && !isTextured ) CLAMPL(shade, 0x1000);
#else // defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
// NOTE: The original game bugfix. We need to limit brightness of untextured faces for DirectX 5
// because brightness of textured faces is limited by D3DTBLEND_MODULATEALPHA or D3DTBLEND_MODULATE
if( !isTextured ) CLAMPL(shade, 0x1000);
#endif // defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
if( shade != 0x1000 ) {
DWORD brightness = 0x2000 - shade;
red = red * brightness >> 12;
green = green * brightness >> 12;
blue = blue * brightness >> 12;
}
CLAMPG(red, 0xFF);
CLAMPG(green, 0xFF);
CLAMPG(blue, 0xFF);
CLAMPG(alpha, 0xFF);
if( IsShadeEffect ) {
#if defined(FEATURE_VIDEOFX_IMPROVED) && defined(FEATURE_MOD_CONFIG)
D3DCOLOR water = GetModWaterColor();
if( CustomWaterColorEnabled && water ) {
red = red * RGB_GETRED(water) / 256;
green = green * RGB_GETGREEN(water) / 256;
blue = blue * RGB_GETBLUE(water) / 256;
} else {
red = red * 128 / 256;
green = green * 224 / 256;
}
#else // defined(FEATURE_VIDEOFX_IMPROVED) && defined(FEATURE_MOD_CONFIG)
red = red * 128 / 256;
green = green * 224 / 256;
#endif // defined(FEATURE_VIDEOFX_IMPROVED) && defined(FEATURE_MOD_CONFIG)
}
return RGBA_MAKE(red, green, blue, alpha);
}
static double CalculatePolyZ(SORTTYPE sortType, double z0, double z1, double z2, double z3 = -1.0) {
double zv = 0.0;
switch( sortType ) {
case ST_AvgZ :
zv = ( z3 > 0.0 ) ? (z0+z1+z2+z3)/4.0 : (z0+z1+z2)/3.0;
break;
case ST_MaxZ :
zv = z0;
CLAMPL(zv, z1);
CLAMPL(zv, z2);
if( z3 > 0.0 ) CLAMPL(zv, z3);
break;
case ST_FarZ :
default :
zv = 4000000000.0; // the original game value was 1000000000.0
break;
}
return zv;
}
#ifdef FEATURE_VIDEOFX_IMPROVED
static POLYTYPE GetPolyType(UINT16 drawtype) {
switch( drawtype ) {
case DRAW_Opaque:
return POLY_HWR_GTmap;
case DRAW_Semitrans:
return AlphaBlendMode ? POLY_HWR_WGTmapHalf : POLY_HWR_WGTmap;
case DRAW_ColorKey:
return POLY_HWR_WGTmap;
}
return POLY_HWR_WGTmap;
}
bool InsertObjectEM(__int16 *ptrObj, int vtxCount, D3DCOLOR tint, PHD_UV *em_uv) {
PHD_VBUF *vtx[4];
PHD_TEXTURE texture;
PHD_UV *uv = texture.uv;
if( ptrObj == NULL || em_uv == NULL || vtxCount < 3 || vtxCount > 4 ) {
return false;
}
texture.drawtype = DRAW_ColorKey;
texture.tpage = (UINT16)~0;
for( int i = 0; i < vtxCount; ++ i ) {
vtx[i] = &PhdVBuf[ptrObj[i]];
texture.uv[i] = em_uv[ptrObj[i]];
}
GlobalTint = tint;
if( vtxCount == 4 ) {
InsertGT4_Sorted(vtx[0], vtx[1], vtx[2], vtx[3], &texture, ST_AvgZ);
} else {
InsertGT3_Sorted(vtx[0], vtx[1], vtx[2], &texture, &uv[0], &uv[1], &uv[2], ST_AvgZ);
}
GlobalTint = 0;
return true;
}
#endif // FEATURE_VIDEOFX_IMPROVED
// NOTE: this function is not presented in the original game
void __cdecl InsertGourQuad(int x0, int y0, int x1, int y1, int z, D3DCOLOR color0, D3DCOLOR color1, D3DCOLOR color2, D3DCOLOR color3) {
double rhw, sz;
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(z);
++Sort3dPtr;
*(Info3dPtr++) = POLY_HWR_trans;
*(Info3dPtr++) = 4; // vertex count
*(D3DTLVERTEX **)Info3dPtr = HWR_VertexPtr;
Info3dPtr += sizeof(D3DTLVERTEX *)/sizeof(__int16);
rhw = RhwFactor / (double)z;
sz = FltResZBuf - rhw * FltResZORhw;
HWR_VertexPtr[0].sx = (float)x1;
HWR_VertexPtr[0].sy = (float)y0;
HWR_VertexPtr[0].color = color1;
HWR_VertexPtr[1].sx = (float)x1;
HWR_VertexPtr[1].sy = (float)y1;
HWR_VertexPtr[1].color = color2;
HWR_VertexPtr[2].sx = (float)x0;
HWR_VertexPtr[2].sy = (float)y1;
HWR_VertexPtr[2].color = color3;
HWR_VertexPtr[3].sx = (float)x0;
HWR_VertexPtr[3].sy = (float)y0;
HWR_VertexPtr[3].color = color0;
for( int i=0; i<4; ++i ) {
HWR_VertexPtr[i].sz = sz;
HWR_VertexPtr[i].rhw = rhw;
}
HWR_VertexPtr += 4;
++SurfaceCount;
}
BOOL __cdecl visible_zclip(PHD_VBUF *vtx0, PHD_VBUF *vtx1, PHD_VBUF *vtx2) {
return ( (vtx0->yv * vtx2->zv - vtx0->zv * vtx2->yv) * vtx1->xv +
(vtx0->zv * vtx2->xv - vtx0->xv * vtx2->zv) * vtx1->yv +
(vtx0->xv * vtx2->yv - vtx0->yv * vtx2->xv) * vtx1->zv < 0.0 );
}
int __cdecl ZedClipper(int vtxCount, POINT_INFO *pts, VERTEX_INFO *vtx) {
int i, j, diff0, diff1;
double clip;
POINT_INFO *pts0, *pts1;
if( vtxCount == 0 )
return 0;
j = 0;
pts0 = pts;
pts1 = &pts[vtxCount-1];
for( i = 0; i < vtxCount; ++i ) {
diff0 = (int)(FltNearZ - pts0->zv);
diff1 = (int)(FltNearZ - pts1->zv);
if( (diff0|diff1) < 0 ) {
if( (diff0^diff1) < 0 ) {
clip = (FltNearZ - pts0->zv) / (pts1->zv - pts0->zv);
vtx[j].x = ((pts1->xv - pts0->xv) * clip + pts0->xv) * FltPerspONearZ + FltWinCenterX;
vtx[j].y = ((pts1->yv - pts0->yv) * clip + pts0->yv) * FltPerspONearZ + FltWinCenterY;
vtx[j].rhw = FltRhwONearZ;
vtx[j].u = ((pts1->u - pts0->u) * clip + pts0->u) * FltRhwONearZ;
vtx[j].v = ((pts1->v - pts0->v) * clip + pts0->v) * FltRhwONearZ;
vtx[j].g = ((pts1->g - pts0->g) * clip + pts0->g);
++j;
}
if( diff0 < 0 ) {
vtx[j].x = pts0->xs;
vtx[j].y = pts0->ys;
vtx[j].rhw = pts0->rhw;
vtx[j].u = pts0->u * pts0->rhw;
vtx[j].v = pts0->v * pts0->rhw;
vtx[j].g = pts0->g;
++j;
}
}
pts1 = pts0++;
}
return ( j < 3 ) ? 0 : j;
}
static inline void clipGUV(VERTEX_INFO *buf, VERTEX_INFO *vtx1, VERTEX_INFO *vtx2, float clip) {
buf->rhw = vtx2->rhw + (vtx1->rhw - vtx2->rhw) * clip;
buf->u = vtx2->u + (vtx1->u - vtx2->u) * clip;
buf->v = vtx2->v + (vtx1->v - vtx2->v) * clip;
buf->g = vtx2->g + (vtx1->g - vtx2->g) * clip;
}
int __cdecl XYGUVClipper(int vtxCount, VERTEX_INFO *vtx) {
VERTEX_INFO vtx_buf[8];
VERTEX_INFO *vtx1, *vtx2;
float clip;
int i, j;
if( vtxCount < 3 )
return 0;
// horizontal clip
vtx2 = &vtx[vtxCount - 1];
j = 0;
for( i = 0; i < vtxCount; ++i ) {
vtx1 = vtx2;
vtx2 = &vtx[i];
if( vtx1->x < FltWinLeft ) {
if( vtx2->x < FltWinLeft ) {
continue;
}
clip = (FltWinLeft - vtx2->x) / (vtx1->x - vtx2->x);
vtx_buf[j].x = FltWinLeft;
vtx_buf[j].y = vtx2->y + (vtx1->y - vtx2->y) * clip;
clipGUV(&vtx_buf[j++], vtx1, vtx2, clip);
}
else if( vtx1->x > FltWinRight) {
if( vtx2->x > FltWinRight ) {
continue;
}
clip = (FltWinRight - vtx2->x) / (vtx1->x - vtx2->x);
vtx_buf[j].x = FltWinRight;
vtx_buf[j].y = vtx2->y + (vtx1->y - vtx2->y) * clip;
clipGUV(&vtx_buf[j++], vtx1, vtx2, clip);
}
if( vtx2->x < FltWinLeft ) {
clip = (FltWinLeft - vtx2->x) / (vtx1->x - vtx2->x);
vtx_buf[j].x = FltWinLeft;
vtx_buf[j].y = vtx2->y + (vtx1->y - vtx2->y) * clip;
clipGUV(&vtx_buf[j++], vtx1, vtx2, clip);
}
else if( vtx2->x > FltWinRight ) {
clip = (FltWinRight - vtx2->x) / (vtx1->x - vtx2->x);
vtx_buf[j].x = FltWinRight;
vtx_buf[j].y = vtx2->y + (vtx1->y - vtx2->y) * clip;
clipGUV(&vtx_buf[j++], vtx1, vtx2, clip);
} else {
vtx_buf[j++] = *vtx2;
}
}
vtxCount = j;
if( vtxCount < 3 )
return 0;
// vertical clip
vtx2 = &vtx_buf[vtxCount-1];
j = 0;
for( i = 0; i < vtxCount; ++i ) {
vtx1 = vtx2;
vtx2 = &vtx_buf[i];
if( vtx1->y < FltWinTop ) {
if( vtx2->y < FltWinTop ) {
continue;
}
clip = (FltWinTop - vtx2->y) / (vtx1->y - vtx2->y);
vtx[j].x = vtx2->x + (vtx1->x - vtx2->x) * clip;
vtx[j].y = FltWinTop;
clipGUV(&vtx[j++], vtx1, vtx2, clip);
}
else if( vtx1->y > FltWinBottom ) {
if( vtx2->y > FltWinBottom ) {
continue;
}
clip = (FltWinBottom - vtx2->y) / (vtx1->y - vtx2->y);
vtx[j].x = vtx2->x + (vtx1->x - vtx2->x) * clip;
vtx[j].y = FltWinBottom;
clipGUV(&vtx[j++], vtx1, vtx2, clip);
}
if( vtx2->y < FltWinTop ) {
clip = (FltWinTop - vtx2->y) / (vtx1->y - vtx2->y);
vtx[j].x = vtx2->x + (vtx1->x - vtx2->x) * clip;
vtx[j].y = FltWinTop;
clipGUV(&vtx[j++], vtx1, vtx2, clip);
}
else if( vtx2->y > FltWinBottom ) {
clip = (FltWinBottom - vtx2->y) / (vtx1->y - vtx2->y);
vtx[j].x = vtx2->x + (vtx1->x - vtx2->x) * clip;
vtx[j].y = FltWinBottom;
clipGUV(&vtx[j++], vtx1, vtx2, clip);
} else {
vtx[j++] = *vtx2;
}
}
return ( j < 3 ) ? 0 : j;
}
__int16 *__cdecl InsertObjectGT4(__int16 *ptrObj, int number, SORTTYPE sortType) {
char clipOR, clipAND;
PHD_VBUF *vtx0, *vtx1, *vtx2, *vtx3;
int i, j, nPoints;
float zv;
__int16 textureIdx;
PHD_TEXTURE *texture;
PHD_UV *uv;
POINT_INFO points[4];
for( i = 0; i < number; ++i ) {
vtx0 = &PhdVBuf[*ptrObj++];
vtx1 = &PhdVBuf[*ptrObj++];
vtx2 = &PhdVBuf[*ptrObj++];
vtx3 = &PhdVBuf[*ptrObj++];
textureIdx = *ptrObj++;
texture = &PhdTextureInfo[textureIdx];
uv = texture->uv;
nPoints = 4;
clipOR = LOBYTE(vtx0->clip | vtx1->clip | vtx2->clip | vtx3->clip);
clipAND = LOBYTE(vtx0->clip & vtx1->clip & vtx2->clip & vtx3->clip);
if( clipAND != 0 )
continue;
if( clipOR >= 0 ) {
if( !VBUF_VISIBLE(*vtx0, *vtx1, *vtx2) )
continue;
if( clipOR == 0 ) {
zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv, vtx3->zv);
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(zv);
++Sort3dPtr;
if( zv >= (double)PerspectiveDistance ) {
*Info3dPtr++ = ( texture->drawtype == DRAW_Opaque ) ? POLY_GTmap : POLY_WGTmap;
*Info3dPtr++ = texture->tpage;
*Info3dPtr++ = 4;
*Info3dPtr++ = (int)vtx0->xs;
*Info3dPtr++ = (int)vtx0->ys;
*Info3dPtr++ = (int)vtx0->g;
*Info3dPtr++ = uv[0].u;
*Info3dPtr++ = uv[0].v;
*Info3dPtr++ = (int)vtx1->xs;
*Info3dPtr++ = (int)vtx1->ys;
*Info3dPtr++ = (int)vtx1->g;
*Info3dPtr++ = uv[1].u;
*Info3dPtr++ = uv[1].v;
*Info3dPtr++ = (int)vtx2->xs;
*Info3dPtr++ = (int)vtx2->ys;
*Info3dPtr++ = (int)vtx2->g;
*Info3dPtr++ = uv[2].u;
*Info3dPtr++ = uv[2].v;
*Info3dPtr++ = (int)vtx3->xs;
*Info3dPtr++ = (int)vtx3->ys;
*Info3dPtr++ = (int)vtx3->g;
*Info3dPtr++ = uv[3].u;
*Info3dPtr++ = uv[3].v;
} else {
*Info3dPtr++ = ( texture->drawtype == DRAW_Opaque ) ? POLY_GTmap_persp : POLY_WGTmap_persp;
*Info3dPtr++ = texture->tpage;
*Info3dPtr++ = 4;
*Info3dPtr++ = (int)vtx0->xs;
*Info3dPtr++ = (int)vtx0->ys;
*Info3dPtr++ = (int)vtx0->g;
*(float *)Info3dPtr = vtx0->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[0].u * vtx0->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[0].v * vtx0->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*Info3dPtr++ = (int)vtx1->xs;
*Info3dPtr++ = (int)vtx1->ys;
*Info3dPtr++ = (int)vtx1->g;
*(float *)Info3dPtr = vtx1->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[1].u * vtx1->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[1].v * vtx1->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*Info3dPtr++ = (int)vtx2->xs;
*Info3dPtr++ = (int)vtx2->ys;
*Info3dPtr++ = (int)vtx2->g;
*(float *)Info3dPtr = vtx2->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[2].u * vtx2->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[2].v * vtx2->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*Info3dPtr++ = (int)vtx3->xs;
*Info3dPtr++ = (int)vtx3->ys;
*Info3dPtr++ = (int)vtx3->g;
*(float *)Info3dPtr = vtx3->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[3].u * vtx3->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[3].v * vtx3->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
}
++SurfaceCount;
continue;
}
VBuffer[0].x = vtx0->xs;
VBuffer[0].y = vtx0->ys;
VBuffer[0].rhw = vtx0->rhw;
VBuffer[0].g = (float)vtx0->g;
VBuffer[0].u = (double)uv[0].u * vtx0->rhw;
VBuffer[0].v = (double)uv[0].v * vtx0->rhw;
VBuffer[1].x = vtx1->xs;
VBuffer[1].y = vtx1->ys;
VBuffer[1].rhw = vtx1->rhw;
VBuffer[1].g = (float)vtx1->g;
VBuffer[1].u = (double)uv[1].u * vtx1->rhw;
VBuffer[1].v = (double)uv[1].v * vtx1->rhw;
VBuffer[2].x = vtx2->xs;
VBuffer[2].y = vtx2->ys;
VBuffer[2].rhw = vtx2->rhw;
VBuffer[2].g = (float)vtx2->g;
VBuffer[2].u = (double)uv[2].u * vtx2->rhw;
VBuffer[2].v = (double)uv[2].v * vtx2->rhw;
VBuffer[3].x = vtx3->xs;
VBuffer[3].y = vtx3->ys;
VBuffer[3].rhw = vtx3->rhw;
VBuffer[3].g = (float)vtx3->g;
VBuffer[3].u = (double)uv[3].u * vtx3->rhw;
VBuffer[3].v = (double)uv[3].v * vtx3->rhw;
} else {
if( !visible_zclip(vtx0, vtx1, vtx2) )
continue;
points[0].xv = vtx0->xv;
points[0].yv = vtx0->yv;
points[0].zv = vtx0->zv;
points[0].rhw = vtx0->rhw;
points[0].xs = vtx0->xs;
points[0].ys = vtx0->ys;
points[0].u = (float)uv[0].u;
points[0].v = (float)uv[0].v;
points[0].g = (float)vtx0->g;
points[1].yv = vtx1->yv;
points[1].xv = vtx1->xv;
points[1].zv = vtx1->zv;
points[1].rhw = vtx1->rhw;
points[1].xs = vtx1->xs;
points[1].ys = vtx1->ys;
points[1].u = (float)uv[1].u;
points[1].v = (float)uv[1].v;
points[1].g = (float)vtx1->g;
points[2].xv = vtx2->xv;
points[2].yv = vtx2->yv;
points[2].zv = vtx2->zv;
points[2].rhw = vtx2->rhw;
points[2].xs = vtx2->xs;
points[2].ys = vtx2->ys;
points[2].u = (float)uv[2].u;
points[2].v = (float)uv[2].v;
points[2].g = (float)vtx2->g;
points[3].xv = vtx3->xv;
points[3].yv = vtx3->yv;
points[3].zv = vtx3->zv;
points[3].rhw = vtx3->rhw;
points[3].xs = vtx3->xs;
points[3].ys = vtx3->ys;
points[3].u = (float)uv[3].u;
points[3].v = (float)uv[3].v;
points[3].g = (float)vtx3->g;
nPoints = ZedClipper(nPoints, points, VBuffer);
if( nPoints == 0 ) continue;
}
nPoints = XYGUVClipper(nPoints, VBuffer);
if( nPoints == 0 ) continue;
zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv, vtx3->zv);
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(zv);
++Sort3dPtr;
if( zv >= (double)PerspectiveDistance ) {
*Info3dPtr++ = ( texture->drawtype == DRAW_Opaque ) ? POLY_GTmap : POLY_WGTmap;
*Info3dPtr++ = texture->tpage;
*Info3dPtr++ = nPoints;
for( j = 0; j < nPoints; ++j ) {
*Info3dPtr++ = (int)VBuffer[j].x;
*Info3dPtr++ = (int)VBuffer[j].y;
*Info3dPtr++ = (int)VBuffer[j].g;
*Info3dPtr++ = (int)(VBuffer[j].u / VBuffer[j].rhw);
*Info3dPtr++ = (int)(VBuffer[j].v / VBuffer[j].rhw);
}
} else {
*Info3dPtr++ = ( texture->drawtype == DRAW_Opaque ) ? POLY_GTmap_persp : POLY_WGTmap_persp;
*Info3dPtr++ = texture->tpage;
*Info3dPtr++ = nPoints;
for( j = 0; j < nPoints; ++j ) {
*Info3dPtr++ = (int)VBuffer[j].x;
*Info3dPtr++ = (int)VBuffer[j].y;
*Info3dPtr++ = (int)VBuffer[j].g;
*(float *)Info3dPtr = VBuffer[j].rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = VBuffer[j].u;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = VBuffer[j].v;
Info3dPtr += sizeof(float)/sizeof(__int16);
}
}
++SurfaceCount;
}
return ptrObj;
}
__int16 *__cdecl InsertObjectGT3(__int16 *ptrObj, int number, SORTTYPE sortType) {
char clipOR, clipAND;
PHD_VBUF *vtx0, *vtx1, *vtx2;
int i, j, nPoints;
float zv;
__int16 textureIdx;
PHD_TEXTURE *texture;
PHD_UV *uv;
POINT_INFO points[3];
for( i = 0; i < number; ++i ) {
vtx0 = &PhdVBuf[*ptrObj++];
vtx1 = &PhdVBuf[*ptrObj++];
vtx2 = &PhdVBuf[*ptrObj++];
textureIdx = *ptrObj++;
texture = &PhdTextureInfo[textureIdx];
uv = texture->uv;
nPoints = 3;
clipOR = LOBYTE(vtx0->clip | vtx1->clip | vtx2->clip);
clipAND = LOBYTE(vtx0->clip & vtx1->clip & vtx2->clip);
if( clipAND != 0 )
continue;
if( clipOR >= 0 ) {
if( !VBUF_VISIBLE(*vtx0, *vtx1, *vtx2) )
continue;
if( clipOR == 0 ) {
zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv);
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(zv);
++Sort3dPtr;
if( zv >= (double)PerspectiveDistance ) {
*Info3dPtr++ = ( texture->drawtype == DRAW_Opaque ) ? POLY_GTmap : POLY_WGTmap;
*Info3dPtr++ = texture->tpage;
*Info3dPtr++ = 3;
*Info3dPtr++ = (int)vtx0->xs;
*Info3dPtr++ = (int)vtx0->ys;
*Info3dPtr++ = (int)vtx0->g;
*Info3dPtr++ = uv[0].u;
*Info3dPtr++ = uv[0].v;
*Info3dPtr++ = (int)vtx1->xs;
*Info3dPtr++ = (int)vtx1->ys;
*Info3dPtr++ = (int)vtx1->g;
*Info3dPtr++ = uv[1].u;
*Info3dPtr++ = uv[1].v;
*Info3dPtr++ = (int)vtx2->xs;
*Info3dPtr++ = (int)vtx2->ys;
*Info3dPtr++ = (int)vtx2->g;
*Info3dPtr++ = uv[2].u;
*Info3dPtr++ = uv[2].v;
} else {
*Info3dPtr++ = ( texture->drawtype == DRAW_Opaque ) ? POLY_GTmap_persp : POLY_WGTmap_persp;
*Info3dPtr++ = texture->tpage;
*Info3dPtr++ = 3;
*Info3dPtr++ = (int)vtx0->xs;
*Info3dPtr++ = (int)vtx0->ys;
*Info3dPtr++ = (int)vtx0->g;
*(float *)Info3dPtr = vtx0->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[0].u * vtx0->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[0].v * vtx0->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*Info3dPtr++ = (int)vtx1->xs;
*Info3dPtr++ = (int)vtx1->ys;
*Info3dPtr++ = (int)vtx1->g;
*(float *)Info3dPtr = vtx1->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[1].u * vtx1->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[1].v * vtx1->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*Info3dPtr++ = (int)vtx2->xs;
*Info3dPtr++ = (int)vtx2->ys;
*Info3dPtr++ = (int)vtx2->g;
*(float *)Info3dPtr = vtx2->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[2].u * vtx2->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = (double)uv[2].v * vtx2->rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
}
++SurfaceCount;
continue;
}
VBuffer[0].x = vtx0->xs;
VBuffer[0].y = vtx0->ys;
VBuffer[0].rhw = vtx0->rhw;
VBuffer[0].g = (float)vtx0->g;
VBuffer[0].u = (double)uv[0].u * vtx0->rhw;
VBuffer[0].v = (double)uv[0].v * vtx0->rhw;
VBuffer[1].x = vtx1->xs;
VBuffer[1].y = vtx1->ys;
VBuffer[1].rhw = vtx1->rhw;
VBuffer[1].g = (float)vtx1->g;
VBuffer[1].u = (double)uv[1].u * vtx1->rhw;
VBuffer[1].v = (double)uv[1].v * vtx1->rhw;
VBuffer[2].x = vtx2->xs;
VBuffer[2].y = vtx2->ys;
VBuffer[2].rhw = vtx2->rhw;
VBuffer[2].g = (float)vtx2->g;
VBuffer[2].u = (double)uv[2].u * vtx2->rhw;
VBuffer[2].v = (double)uv[2].v * vtx2->rhw;
} else {
if( !visible_zclip(vtx0, vtx1, vtx2) )
continue;
points[0].xv = vtx0->xv;
points[0].yv = vtx0->yv;
points[0].zv = vtx0->zv;
points[0].rhw = vtx0->rhw;
points[0].xs = vtx0->xs;
points[0].ys = vtx0->ys;
points[0].u = (float)uv[0].u;
points[0].v = (float)uv[0].v;
points[0].g = (float)vtx0->g;
points[1].yv = vtx1->yv;
points[1].xv = vtx1->xv;
points[1].zv = vtx1->zv;
points[1].rhw = vtx1->rhw;
points[1].xs = vtx1->xs;
points[1].ys = vtx1->ys;
points[1].u = (float)uv[1].u;
points[1].v = (float)uv[1].v;
points[1].g = (float)vtx1->g;
points[2].xv = vtx2->xv;
points[2].yv = vtx2->yv;
points[2].zv = vtx2->zv;
points[2].rhw = vtx2->rhw;
points[2].xs = vtx2->xs;
points[2].ys = vtx2->ys;
points[2].u = (float)uv[2].u;
points[2].v = (float)uv[2].v;
points[2].g = (float)vtx2->g;
nPoints = ZedClipper(nPoints, points, VBuffer);
if( nPoints == 0 ) continue;
}
nPoints = XYGUVClipper(nPoints, VBuffer);
if( nPoints == 0 ) continue;
zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv);
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(zv);
++Sort3dPtr;
if( zv >= (double)PerspectiveDistance ) {
*Info3dPtr++ = ( texture->drawtype == DRAW_Opaque ) ? POLY_GTmap : POLY_WGTmap;
*Info3dPtr++ = texture->tpage;
*Info3dPtr++ = nPoints;
for( j = 0; j < nPoints; ++j ) {
*Info3dPtr++ = (int)VBuffer[j].x;
*Info3dPtr++ = (int)VBuffer[j].y;
*Info3dPtr++ = (int)VBuffer[j].g;
*Info3dPtr++ = (int)(VBuffer[j].u / VBuffer[j].rhw);
*Info3dPtr++ = (int)(VBuffer[j].v / VBuffer[j].rhw);
}
} else {
*Info3dPtr++ = ( texture->drawtype == DRAW_Opaque ) ? POLY_GTmap_persp : POLY_WGTmap_persp;
*Info3dPtr++ = texture->tpage;
*Info3dPtr++ = nPoints;
for( j = 0; j < nPoints; ++j ) {
*Info3dPtr++ = (int)VBuffer[j].x;
*Info3dPtr++ = (int)VBuffer[j].y;
*Info3dPtr++ = (int)VBuffer[j].g;
*(float *)Info3dPtr = VBuffer[j].rhw;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = VBuffer[j].u;
Info3dPtr += sizeof(float)/sizeof(__int16);
*(float *)Info3dPtr = VBuffer[j].v;
Info3dPtr += sizeof(float)/sizeof(__int16);
}
}
++SurfaceCount;
}
return ptrObj;
}
static inline void clipG(VERTEX_INFO *buf, VERTEX_INFO *vtx1, VERTEX_INFO *vtx2, float clip) {
buf->rhw = vtx2->rhw + (vtx1->rhw - vtx2->rhw) * clip;
buf->g = vtx2->g + (vtx1->g - vtx2->g) * clip;
}
int __cdecl XYGClipper(int vtxCount, VERTEX_INFO *vtx) {
VERTEX_INFO vtx_buf[8];
VERTEX_INFO *vtx1, *vtx2;
float clip;
int i, j;
if( vtxCount < 3 )
return 0;
// horizontal clip
vtx2 = &vtx[vtxCount - 1];
j = 0;
for( i = 0; i < vtxCount; ++i ) {
vtx1 = vtx2;
vtx2 = &vtx[i];
if( vtx1->x < FltWinLeft ) {
if( vtx2->x < FltWinLeft ) {
continue;
}
clip = (FltWinLeft - vtx2->x) / (vtx1->x - vtx2->x);
vtx_buf[j].x = FltWinLeft;
vtx_buf[j].y = vtx2->y + (vtx1->y - vtx2->y) * clip;
clipG(&vtx_buf[j++], vtx1, vtx2, clip);
}
else if( vtx1->x > FltWinRight) {
if( vtx2->x > FltWinRight ) {
continue;
}
clip = (FltWinRight - vtx2->x) / (vtx1->x - vtx2->x);
vtx_buf[j].x = FltWinRight;
vtx_buf[j].y = vtx2->y + (vtx1->y - vtx2->y) * clip;
clipG(&vtx_buf[j++], vtx1, vtx2, clip);
}
if( vtx2->x < FltWinLeft ) {
clip = (FltWinLeft - vtx2->x) / (vtx1->x - vtx2->x);
vtx_buf[j].x = FltWinLeft;
vtx_buf[j].y = vtx2->y + (vtx1->y - vtx2->y) * clip;
clipG(&vtx_buf[j++], vtx1, vtx2, clip);
}
else if( vtx2->x > FltWinRight ) {
clip = (FltWinRight - vtx2->x) / (vtx1->x - vtx2->x);
vtx_buf[j].x = FltWinRight;
vtx_buf[j].y = vtx2->y + (vtx1->y - vtx2->y) * clip;
clipG(&vtx_buf[j++], vtx1, vtx2, clip);
} else {
vtx_buf[j++] = *vtx2;
}
}
vtxCount = j;
if( vtxCount < 3 )
return 0;
// vertical clip
vtx2 = &vtx_buf[vtxCount-1];
j = 0;
for( i = 0; i < vtxCount; ++i ) {
vtx1 = vtx2;
vtx2 = &vtx_buf[i];
if( vtx1->y < FltWinTop ) {
if( vtx2->y < FltWinTop ) {
continue;
}
clip = (FltWinTop - vtx2->y) / (vtx1->y - vtx2->y);
vtx[j].x = vtx2->x + (vtx1->x - vtx2->x) * clip;
vtx[j].y = FltWinTop;
clipG(&vtx[j++], vtx1, vtx2, clip);
}
else if( vtx1->y > FltWinBottom ) {
if( vtx2->y > FltWinBottom ) {
continue;
}
clip = (FltWinBottom - vtx2->y) / (vtx1->y - vtx2->y);
vtx[j].x = vtx2->x + (vtx1->x - vtx2->x) * clip;
vtx[j].y = FltWinBottom;
clipG(&vtx[j++], vtx1, vtx2, clip);
}
if( vtx2->y < FltWinTop ) {
clip = (FltWinTop - vtx2->y) / (vtx1->y - vtx2->y);
vtx[j].x = vtx2->x + (vtx1->x - vtx2->x) * clip;
vtx[j].y = FltWinTop;
clipG(&vtx[j++], vtx1, vtx2, clip);
}
else if( vtx2->y > FltWinBottom ) {
clip = (FltWinBottom - vtx2->y) / (vtx1->y - vtx2->y);
vtx[j].x = vtx2->x + (vtx1->x - vtx2->x) * clip;
vtx[j].y = FltWinBottom;
clipG(&vtx[j++], vtx1, vtx2, clip);
} else {
vtx[j++] = *vtx2;
}
}
return ( j < 3 ) ? 0 : j;
}
__int16 *__cdecl InsertObjectG4(__int16 *ptrObj, int number, SORTTYPE sortType) {
char clipOR, clipAND;
PHD_VBUF *vtx0, *vtx1, *vtx2, *vtx3;
int i, j, nPoints;
float zv;
BYTE colorIdx;
POINT_INFO pts[4];
for( i = 0; i < number; ++i ) {
vtx0 = &PhdVBuf[*ptrObj++];
vtx1 = &PhdVBuf[*ptrObj++];
vtx2 = &PhdVBuf[*ptrObj++];
vtx3 = &PhdVBuf[*ptrObj++];
colorIdx = *ptrObj++;
nPoints = 4;
clipOR = LOBYTE(vtx0->clip | vtx1->clip | vtx2->clip | vtx3->clip);
clipAND = LOBYTE(vtx0->clip & vtx1->clip & vtx2->clip & vtx3->clip);
if( clipAND != 0 )
continue;
if( clipOR >= 0 ) {
if( !VBUF_VISIBLE(*vtx0, *vtx1, *vtx2) )
continue;
VBuffer[0].x = vtx0->xs;
VBuffer[0].y = vtx0->ys;
VBuffer[0].rhw = vtx0->rhw;
VBuffer[0].g = (float)vtx0->g;
VBuffer[1].x = vtx1->xs;
VBuffer[1].y = vtx1->ys;
VBuffer[1].rhw = vtx1->rhw;
VBuffer[1].g = (float)vtx1->g;
VBuffer[2].x = vtx2->xs;
VBuffer[2].y = vtx2->ys;
VBuffer[2].rhw = vtx2->rhw;
VBuffer[2].g = (float)vtx2->g;
VBuffer[3].x = vtx3->xs;
VBuffer[3].y = vtx3->ys;
VBuffer[3].rhw = vtx3->rhw;
VBuffer[3].g = (float)vtx3->g;
if( clipOR > 0 ) {
nPoints = XYGClipper(nPoints, VBuffer);
}
} else {
if( !visible_zclip(vtx0, vtx1, vtx2) )
continue;
pts[0].xv = vtx0->xv;
pts[0].yv = vtx0->yv;
pts[0].zv = vtx0->zv;
pts[0].rhw = vtx0->rhw;
pts[0].xs = vtx0->xs;
pts[0].ys = vtx0->ys;
pts[0].g = (float)vtx0->g;
pts[1].xv = vtx1->xv;
pts[1].yv = vtx1->yv;
pts[1].zv = vtx1->zv;
pts[1].rhw = vtx1->rhw;
pts[1].xs = vtx1->xs;
pts[1].ys = vtx1->ys;
pts[1].g = (float)vtx1->g;
pts[2].xv = vtx2->xv;
pts[2].yv = vtx2->yv;
pts[2].zv = vtx2->zv;
pts[2].rhw = vtx2->rhw;
pts[2].xs = vtx2->xs;
pts[2].ys = vtx2->ys;
pts[2].g = (float)vtx2->g;
pts[3].xv = vtx3->xv;
pts[3].yv = vtx3->yv;
pts[3].zv = vtx3->zv;
pts[3].rhw = vtx3->rhw;
pts[3].xs = vtx3->xs;
pts[3].ys = vtx3->ys;
pts[3].g = (float)vtx3->g;
nPoints = ZedClipper(nPoints, pts, VBuffer);
if( nPoints == 0 ) continue;
nPoints = XYGClipper(nPoints, VBuffer);
}
if( nPoints == 0 )
continue;
zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv, vtx3->zv);
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(zv);
++Sort3dPtr;
*Info3dPtr++ = POLY_gouraud;
*Info3dPtr++ = colorIdx;
*Info3dPtr++ = nPoints;
for( j = 0; j < nPoints; ++j ) {
*Info3dPtr++ = VBuffer[j].x;
*Info3dPtr++ = VBuffer[j].y;
*Info3dPtr++ = VBuffer[j].g;
}
++SurfaceCount;
}
return ptrObj;
}
__int16 *__cdecl InsertObjectG3(__int16 *ptrObj, int number, SORTTYPE sortType) {
char clipOR, clipAND;
PHD_VBUF *vtx0, *vtx1, *vtx2;
int i, j, nPoints;
float zv;
BYTE colorIdx;
POINT_INFO pts[3];
for( i = 0; i < number; ++i ) {
vtx0 = &PhdVBuf[*ptrObj++];
vtx1 = &PhdVBuf[*ptrObj++];
vtx2 = &PhdVBuf[*ptrObj++];
colorIdx = *ptrObj++;
nPoints = 3;
clipOR = LOBYTE(vtx0->clip | vtx1->clip | vtx2->clip);
clipAND = LOBYTE(vtx0->clip & vtx1->clip & vtx2->clip);
if( clipAND != 0 )
continue;
if( clipOR >= 0 ) {
if( !VBUF_VISIBLE(*vtx0, *vtx1, *vtx2) )
continue;
VBuffer[0].x = vtx0->xs;
VBuffer[0].y = vtx0->ys;
VBuffer[0].rhw = vtx0->rhw;
VBuffer[0].g = (float)vtx0->g;
VBuffer[1].x = vtx1->xs;
VBuffer[1].y = vtx1->ys;
VBuffer[1].rhw = vtx1->rhw;
VBuffer[1].g = (float)vtx1->g;
VBuffer[2].x = vtx2->xs;
VBuffer[2].y = vtx2->ys;
VBuffer[2].rhw = vtx2->rhw;
VBuffer[2].g = (float)vtx2->g;
if( clipOR > 0 ) {
nPoints = XYGClipper(nPoints, VBuffer);
}
} else {
if( !visible_zclip(vtx0, vtx1, vtx2) )
continue;
pts[0].xv = vtx0->xv;
pts[0].yv = vtx0->yv;
pts[0].zv = vtx0->zv;
pts[0].rhw = vtx0->rhw;
pts[0].xs = vtx0->xs;
pts[0].ys = vtx0->ys;
pts[0].g = (float)vtx0->g;
pts[1].xv = vtx1->xv;
pts[1].yv = vtx1->yv;
pts[1].zv = vtx1->zv;
pts[1].rhw = vtx1->rhw;
pts[1].xs = vtx1->xs;
pts[1].ys = vtx1->ys;
pts[1].g = (float)vtx1->g;
pts[2].xv = vtx2->xv;
pts[2].yv = vtx2->yv;
pts[2].zv = vtx2->zv;
pts[2].rhw = vtx2->rhw;
pts[2].xs = vtx2->xs;
pts[2].ys = vtx2->ys;
pts[2].g = (float)vtx2->g;
nPoints = ZedClipper(nPoints, pts, VBuffer);
if( nPoints == 0 ) continue;
nPoints = XYGClipper(nPoints, VBuffer);
}
if( nPoints == 0 )
continue;
zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv);
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(zv);
++Sort3dPtr;
*Info3dPtr++ = POLY_gouraud;
*Info3dPtr++ = colorIdx;
*Info3dPtr++ = nPoints;
for( j = 0; j < nPoints; ++j ) {
*Info3dPtr++ = (int)VBuffer[j].x;
*Info3dPtr++ = (int)VBuffer[j].y;
*Info3dPtr++ = (int)VBuffer[j].g;
}
++SurfaceCount;
}
return ptrObj;
}
int __cdecl XYClipper(int vtxCount, VERTEX_INFO *vtx) {
// NOTE: the original function ignores rhw clipping that produces bugs for Z Buffer
static VERTEX_INFO vtx_buf[40]; // NOTE: original size was 20
VERTEX_INFO *vtx1, *vtx2;
float clip;
int i, j;
if( vtxCount < 3 )
return 0;
// horizontal clip
vtx2 = &vtx[vtxCount - 1];
j = 0;
for( i = 0; i < vtxCount; ++i ) {
vtx1 = vtx2;
vtx2 = &vtx[i];
if( vtx1->x < FltWinLeft ) {
if( vtx2->x < FltWinLeft ) {
continue;
}
clip = (FltWinLeft - vtx2->x) / (vtx1->x - vtx2->x);
vtx_buf[j].x = FltWinLeft;
vtx_buf[j].y = vtx2->y + (vtx1->y - vtx2->y) * clip;
vtx_buf[j++].rhw = vtx2->rhw + (vtx1->rhw - vtx2->rhw) * clip;
}
else if( vtx1->x > FltWinRight) {
if( vtx2->x > FltWinRight ) {
continue;
}
clip = (FltWinRight - vtx2->x) / (vtx1->x - vtx2->x);
vtx_buf[j].x = FltWinRight;
vtx_buf[j].y = vtx2->y + (vtx1->y - vtx2->y) * clip;
vtx_buf[j++].rhw = vtx2->rhw + (vtx1->rhw - vtx2->rhw) * clip;
}
if( vtx2->x < FltWinLeft ) {
clip = (FltWinLeft - vtx2->x) / (vtx1->x - vtx2->x);
vtx_buf[j].x = FltWinLeft;
vtx_buf[j].y = vtx2->y + (vtx1->y - vtx2->y) * clip;
vtx_buf[j++].rhw = vtx2->rhw + (vtx1->rhw - vtx2->rhw) * clip;
}
else if( vtx2->x > FltWinRight ) {
clip = (FltWinRight - vtx2->x) / (vtx1->x - vtx2->x);
vtx_buf[j].x = FltWinRight;
vtx_buf[j].y = vtx2->y + (vtx1->y - vtx2->y) * clip;
vtx_buf[j++].rhw = vtx2->rhw + (vtx1->rhw - vtx2->rhw) * clip;
} else {
vtx_buf[j].x = vtx2->x;
vtx_buf[j].y = vtx2->y;
vtx_buf[j++].rhw = vtx2->rhw;
}
}
vtxCount = j;
if( vtxCount < 3 )
return 0;
// vertical clip
vtx2 = &vtx_buf[vtxCount-1];
j = 0;
for( i = 0; i < vtxCount; ++i ) {
vtx1 = vtx2;
vtx2 = &vtx_buf[i];
if( vtx1->y < FltWinTop ) {
if( vtx2->y < FltWinTop ) {
continue;
}
clip = (FltWinTop - vtx2->y) / (vtx1->y - vtx2->y);
vtx[j].x = vtx2->x + (vtx1->x - vtx2->x) * clip;
vtx[j].y = FltWinTop;
vtx[j++].rhw = vtx2->rhw + (vtx1->rhw - vtx2->rhw) * clip;
}
else if( vtx1->y > FltWinBottom ) {
if( vtx2->y > FltWinBottom ) {
continue;
}
clip = (FltWinBottom - vtx2->y) / (vtx1->y - vtx2->y);
vtx[j].x = vtx2->x + (vtx1->x - vtx2->x) * clip;
vtx[j].y = FltWinBottom;
vtx[j++].rhw = vtx2->rhw + (vtx1->rhw - vtx2->rhw) * clip;
}
if( vtx2->y < FltWinTop ) {
clip = (FltWinTop - vtx2->y) / (vtx1->y - vtx2->y);
vtx[j].x = vtx2->x + (vtx1->x - vtx2->x) * clip;
vtx[j].y = FltWinTop;
vtx[j++].rhw = vtx2->rhw + (vtx1->rhw - vtx2->rhw) * clip;
}
else if( vtx2->y > FltWinBottom ) {
clip = (FltWinBottom - vtx2->y) / (vtx1->y - vtx2->y);
vtx[j].x = vtx2->x + (vtx1->x - vtx2->x) * clip;
vtx[j].y = FltWinBottom;
vtx[j++].rhw = vtx2->rhw + (vtx1->rhw - vtx2->rhw) * clip;
} else {
vtx[j].x = vtx2->x;
vtx[j].y = vtx2->y;
vtx[j++].rhw = vtx2->rhw;
}
}
return ( j < 3 ) ? 0 : j;
}
void __cdecl InsertTrans8(PHD_VBUF *vbuf, __int16 shade) {
int i, nPoints, polyZ;
char clipOR = 0x00;
char clipAND = 0xFF;
#ifdef FEATURE_VIDEOFX_IMPROVED
int nVtx = ( ShadowMode == 1 ) ? 32 : 8;
#else // FEATURE_VIDEOFX_IMPROVED
int nVtx = 8;
#endif // FEATURE_VIDEOFX_IMPROVED
for( i = 0; i < nVtx; ++i ) {
clipOR |= LOBYTE(vbuf[i].clip);
clipAND &= LOBYTE(vbuf[i].clip);
}
if( (clipOR < 0) || (clipAND != 0) || !VBUF_VISIBLE(vbuf[0], vbuf[1], vbuf[2]) )
return;
for( i = 0; i < nVtx; ++i ) {
VBuffer[i].x = vbuf[i].xs;
VBuffer[i].y = vbuf[i].ys;
}
nPoints = nVtx;
if( clipOR != 0 ) {
FltWinLeft = 0.0;
FltWinTop = 0.0;
FltWinRight = (float)PhdWinMaxX;
FltWinBottom = (float)PhdWinMaxY;
nPoints = XYClipper(nPoints, VBuffer);
if( nPoints == 0) return;
}
#ifdef FEATURE_VIDEOFX_IMPROVED
double polyZflt = 0.0;
for( i = 0; i < nVtx; ++i ) {
polyZflt += (double)vbuf[i].zv / (double)nVtx;
}
polyZ = polyZflt;
#else // FEATURE_VIDEOFX_IMPROVED
polyZ = 0;
for( i = 0; i < nVtx; ++i ) {
polyZ += vbuf[i].zv;
}
polyZ /= nVtx;
#endif // FEATURE_VIDEOFX_IMPROVED
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(polyZ);
++Sort3dPtr;
*(Info3dPtr++) = POLY_trans;
*(Info3dPtr++) = shade;
*(Info3dPtr++) = nPoints; // number of vertices
for( i = 0; i < nPoints; ++i ) {
*(Info3dPtr++) = (int)VBuffer[i].x;
*(Info3dPtr++) = (int)VBuffer[i].y;
}
++SurfaceCount;
}
void __cdecl InsertTransQuad(int x, int y, int width, int height, int z) {
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(PhdNearZ + 8*z);
++Sort3dPtr;
*(Info3dPtr++) = POLY_trans;
// NOTE: Here 24 is DepthQ index (shade factor).
// 0 lightest, 15 no shade, 31 darkest (pitch black).
// But original code has value 32 supposed to be interpreted as 24 (which means 50% darker)
// Also 32 is maximum valid value in the original code, though it is DepthQTable range violation.
// This trick worked because DepthQIndex array was right after DepthQ array in the memory
// (DepthQIndex is equal to &DepthQ[24].index).This allocation is not guaranteed on some systems, so it was fixed
*(Info3dPtr++) = 24;
*(Info3dPtr++) = 4; // number of vertices
*(Info3dPtr++) = x;
*(Info3dPtr++) = y;
*(Info3dPtr++) = x + width;
*(Info3dPtr++) = y;
*(Info3dPtr++) = x + width;
*(Info3dPtr++) = y + height;
*(Info3dPtr++) = x;
*(Info3dPtr++) = y + height;
++SurfaceCount;
}
void __cdecl InsertFlatRect(int x0, int y0, int x1, int y1, int z, BYTE colorIdx) {
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(z);
++Sort3dPtr;
*(Info3dPtr++) = POLY_flat;
*(Info3dPtr++) = colorIdx;
*(Info3dPtr++) = 4; // number of vertices
*(Info3dPtr++) = x0;
*(Info3dPtr++) = y0;
*(Info3dPtr++) = x1;
*(Info3dPtr++) = y0;
*(Info3dPtr++) = x1;
*(Info3dPtr++) = y1;
*(Info3dPtr++) = x0;
*(Info3dPtr++) = y1;
++SurfaceCount;
}
void __cdecl InsertLine(int x0, int y0, int x1, int y1, int z, BYTE colorIdx) {
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(z);
++Sort3dPtr;
*(Info3dPtr++) = POLY_line;
*(Info3dPtr++) = PhdWinMinX + x0;
*(Info3dPtr++) = PhdWinMinY + y0;
*(Info3dPtr++) = PhdWinMinX + x1;
*(Info3dPtr++) = PhdWinMinY + y1;
*(Info3dPtr++) = colorIdx;
++SurfaceCount;
}
void __cdecl InsertGT3_ZBuffered(PHD_VBUF *vtx0, PHD_VBUF *vtx1, PHD_VBUF *vtx2, PHD_TEXTURE *texture, PHD_UV *uv0, PHD_UV *uv1, PHD_UV *uv2) {
char clipOR, clipAND;
POINT_INFO points[3];
int nPoints = 3;
clipOR = LOBYTE(vtx0->clip | vtx1->clip | vtx2->clip);
clipAND = LOBYTE(vtx0->clip & vtx1->clip & vtx2->clip);
if( clipAND != 0 )
return;
if( clipOR >= 0 ) {
if( !VBUF_VISIBLE(*vtx0, *vtx1, *vtx2) )
return;
if( clipOR == 0 ) {
VBufferD3D[0].sx = vtx0->xs;
VBufferD3D[0].sy = vtx0->ys;
VBufferD3D[0].sz = FltResZBuf - FltResZORhw * vtx0->rhw;
VBufferD3D[0].rhw = vtx0->rhw;
VBufferD3D[0].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx0->g, true);
VBufferD3D[0].tu = (double)uv0->u / (double)PHD_ONE;
VBufferD3D[0].tv = (double)uv0->v / (double)PHD_ONE;
VBufferD3D[1].sx = vtx1->xs;
VBufferD3D[1].sy = vtx1->ys;
VBufferD3D[1].sz = FltResZBuf - FltResZORhw * vtx1->rhw;
VBufferD3D[1].rhw = vtx1->rhw;
VBufferD3D[1].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx1->g, true);
VBufferD3D[1].tu = (double)uv1->u / (double)PHD_ONE;
VBufferD3D[1].tv = (double)uv1->v / (double)PHD_ONE;
VBufferD3D[2].sx = vtx2->xs;
VBufferD3D[2].sy = vtx2->ys;
VBufferD3D[2].sz = FltResZBuf - FltResZORhw * vtx2->rhw;
VBufferD3D[2].rhw = vtx2->rhw;
VBufferD3D[2].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx2->g, true);
VBufferD3D[2].tu = (double)uv2->u / (double)PHD_ONE;
VBufferD3D[2].tv = (double)uv2->v / (double)PHD_ONE;
#ifdef FEATURE_VIDEOFX_IMPROVED
HWR_TexSource(texture->tpage == (UINT16)~0 ? GetEnvmapTextureHandle() : HWR_PageHandles[texture->tpage]);
#else // !FEATURE_VIDEOFX_IMPROVED
HWR_TexSource(HWR_PageHandles[texture->tpage]);
#endif // !FEATURE_VIDEOFX_IMPROVED
HWR_EnableColorKey(texture->drawtype != DRAW_Opaque);
HWR_DrawPrimitive(D3DPT_TRIANGLELIST, VBufferD3D, 3, true);
return;
}
VBuffer[0].x = vtx0->xs;
VBuffer[0].y = vtx0->ys;
VBuffer[0].rhw = vtx0->rhw;
VBuffer[0].g = (double)vtx0->g;
VBuffer[0].u = (double)uv0->u * vtx0->rhw;
VBuffer[0].v = (double)uv0->v * vtx0->rhw;
VBuffer[1].x = vtx1->xs;
VBuffer[1].y = vtx1->ys;
VBuffer[1].rhw = vtx1->rhw;
VBuffer[1].g = (double)vtx1->g;
VBuffer[1].u = (double)uv1->u * vtx1->rhw;
VBuffer[1].v = (double)uv1->v * vtx1->rhw;
VBuffer[2].x = vtx2->xs;
VBuffer[2].y = vtx2->ys;
VBuffer[2].rhw = vtx2->rhw;
VBuffer[2].g = (double)vtx2->g;
VBuffer[2].u = (double)uv2->u * vtx2->rhw;
VBuffer[2].v = (double)uv2->v * vtx2->rhw;
} else {
if( !visible_zclip(vtx0, vtx1, vtx2) )
return;
points[0].xv = vtx0->xv;
points[0].yv = vtx0->yv;
points[0].zv = vtx0->zv;
points[0].rhw = vtx0->rhw;
points[0].xs = vtx0->xs;
points[0].ys = vtx0->ys;
points[0].u = (float)uv0->u;
points[0].v = (float)uv0->v;
points[0].g = (float)vtx0->g;
points[1].yv = vtx1->yv;
points[1].xv = vtx1->xv;
points[1].zv = vtx1->zv;
points[1].rhw = vtx1->rhw;
points[1].xs = vtx1->xs;
points[1].ys = vtx1->ys;
points[1].u = (float)uv1->u;
points[1].v = (float)uv1->v;
points[1].g = (float)vtx1->g;
points[2].xv = vtx2->xv;
points[2].yv = vtx2->yv;
points[2].zv = vtx2->zv;
points[2].rhw = vtx2->rhw;
points[2].xs = vtx2->xs;
points[2].ys = vtx2->ys;
points[2].u = (float)uv2->u;
points[2].v = (float)uv2->v;
points[2].g = (float)vtx2->g;
nPoints = ZedClipper(nPoints, points, VBuffer);
if( nPoints == 0 ) return;
}
nPoints = XYGUVClipper(nPoints, VBuffer);
if( nPoints == 0 ) return;
#ifdef FEATURE_VIDEOFX_IMPROVED
HWR_TexSource(texture->tpage == (UINT16)~0 ? GetEnvmapTextureHandle() : HWR_PageHandles[texture->tpage]);
#else // !FEATURE_VIDEOFX_IMPROVED
HWR_TexSource(HWR_PageHandles[texture->tpage]);
#endif // !FEATURE_VIDEOFX_IMPROVED
HWR_EnableColorKey(texture->drawtype != DRAW_Opaque);
DrawClippedPoly_Textured(nPoints);
}
void __cdecl DrawClippedPoly_Textured(int vtxCount) {
D3DCOLOR color;
double tu, tv;
if( vtxCount == 0 )
return;
for( int i = 0; i < vtxCount; ++i ) {
color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, (DWORD)VBuffer[i].g, true);
tu = VBuffer[i].u / VBuffer[i].rhw / (double)PHD_ONE;
tv = VBuffer[i].v / VBuffer[i].rhw / (double)PHD_ONE;
CLAMP(tu, 0.0, 1.0);
CLAMP(tv, 0.0, 1.0);
VBufferD3D[i].sx = VBuffer[i].x;
VBufferD3D[i].sy = VBuffer[i].y;
VBufferD3D[i].sz = FltResZBuf - FltResZORhw * VBuffer[i].rhw;
VBufferD3D[i].rhw = VBuffer[i].rhw;
VBufferD3D[i].color = color;
VBufferD3D[i].tu = tu;
VBufferD3D[i].tv = tv;
}
HWR_DrawPrimitive(D3DPT_TRIANGLEFAN, VBufferD3D, vtxCount, true);
}
void __cdecl InsertGT4_ZBuffered(PHD_VBUF *vtx0, PHD_VBUF *vtx1, PHD_VBUF *vtx2, PHD_VBUF *vtx3, PHD_TEXTURE *texture) {
char clipOR, clipAND;
clipOR = LOBYTE(vtx0->clip | vtx1->clip | vtx2->clip | vtx3->clip);
clipAND = LOBYTE(vtx0->clip & vtx1->clip & vtx2->clip & vtx3->clip);
if( clipAND != 0 )
return;
if( clipOR == 0 && VBUF_VISIBLE(*vtx0, *vtx1, *vtx2) ) {
VBufferD3D[0].sx = vtx0->xs;
VBufferD3D[0].sy = vtx0->ys;
VBufferD3D[0].sz = FltResZBuf - FltResZORhw * vtx0->rhw;
VBufferD3D[0].rhw = vtx0->rhw;
VBufferD3D[0].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx0->g, true);
VBufferD3D[0].tu = (double)texture->uv[0].u / (double)PHD_ONE;
VBufferD3D[0].tv = (double)texture->uv[0].v / (double)PHD_ONE;
VBufferD3D[1].sx = vtx1->xs;
VBufferD3D[1].sy = vtx1->ys;
VBufferD3D[1].sz = FltResZBuf - FltResZORhw * vtx1->rhw;
VBufferD3D[1].rhw = vtx1->rhw;
VBufferD3D[1].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx1->g, true);
VBufferD3D[1].tu = (double)texture->uv[1].u / (double)PHD_ONE;
VBufferD3D[1].tv = (double)texture->uv[1].v / (double)PHD_ONE;
VBufferD3D[2].sx = vtx2->xs;
VBufferD3D[2].sy = vtx2->ys;
VBufferD3D[2].sz = FltResZBuf - FltResZORhw * vtx2->rhw;
VBufferD3D[2].rhw = vtx2->rhw;
VBufferD3D[2].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx2->g, true);
VBufferD3D[2].tu = (double)texture->uv[2].u / (double)PHD_ONE;
VBufferD3D[2].tv = (double)texture->uv[2].v / (double)PHD_ONE;
VBufferD3D[3].sx = vtx3->xs;
VBufferD3D[3].sy = vtx3->ys;
VBufferD3D[3].sz = FltResZBuf - FltResZORhw * vtx3->rhw;
VBufferD3D[3].rhw = vtx3->rhw;
VBufferD3D[3].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx3->g, true);
VBufferD3D[3].tu = (double)texture->uv[3].u / (double)PHD_ONE;
VBufferD3D[3].tv = (double)texture->uv[3].v / (double)PHD_ONE;
#ifdef FEATURE_VIDEOFX_IMPROVED
HWR_TexSource(texture->tpage == (UINT16)~0 ? GetEnvmapTextureHandle() : HWR_PageHandles[texture->tpage]);
#else // !FEATURE_VIDEOFX_IMPROVED
HWR_TexSource(HWR_PageHandles[texture->tpage]);
#endif // !FEATURE_VIDEOFX_IMPROVED
HWR_EnableColorKey(texture->drawtype != DRAW_Opaque);
HWR_DrawPrimitive(D3DPT_TRIANGLEFAN, VBufferD3D, 4, true);
}
else if( (clipOR < 0 && visible_zclip(vtx0, vtx1, vtx2)) ||
(clipOR > 0 && VBUF_VISIBLE(*vtx0, *vtx1, *vtx2)) )
{
InsertGT3_ZBuffered(vtx0, vtx1, vtx2, texture, texture->uv, &texture->uv[1], &texture->uv[2]);
InsertGT3_ZBuffered(vtx0, vtx2, vtx3, texture, texture->uv, &texture->uv[2], &texture->uv[3]);
}
}
__int16 *__cdecl InsertObjectGT4_ZBuffered(__int16 *ptrObj, int number, SORTTYPE sortType) {
PHD_VBUF *vtx0, *vtx1, *vtx2, *vtx3;
PHD_TEXTURE *texture;
for( int i = 0; i < number; ++i ) {
vtx0 = &PhdVBuf[ptrObj[0]];
vtx1 = &PhdVBuf[ptrObj[1]];
vtx2 = &PhdVBuf[ptrObj[2]];
vtx3 = &PhdVBuf[ptrObj[3]];
texture = &PhdTextureInfo[ptrObj[4]];
if( texture->drawtype != DRAW_Opaque )
InsertGT4_Sorted(vtx0, vtx1, vtx2, vtx3, texture, sortType);
else
InsertGT4_ZBuffered(vtx0, vtx1, vtx2, vtx3, texture);
ptrObj += 5;
}
return ptrObj;
}
__int16 *__cdecl InsertObjectGT3_ZBuffered(__int16 *ptrObj, int number, SORTTYPE sortType) {
PHD_VBUF *vtx0, *vtx1, *vtx2;
PHD_TEXTURE *texture;
PHD_UV *uv;
for( int i = 0; i < number; ++i ) {
vtx0 = &PhdVBuf[ptrObj[0]];
vtx1 = &PhdVBuf[ptrObj[1]];
vtx2 = &PhdVBuf[ptrObj[2]];
texture = &PhdTextureInfo[ptrObj[3]];
uv = texture->uv;
if( texture->drawtype != DRAW_Opaque )
InsertGT3_Sorted(vtx0, vtx1, vtx2, texture, &uv[0], &uv[1], &uv[2], sortType);
else
InsertGT3_ZBuffered(vtx0, vtx1, vtx2, texture, &uv[0], &uv[1], &uv[2]);
ptrObj += 4;
}
return ptrObj;
}
__int16 *__cdecl InsertObjectG4_ZBuffered(__int16 *ptrObj, int number, SORTTYPE sortType) {
char clipOR, clipAND;
PHD_VBUF *vtx0, *vtx1, *vtx2, *vtx3;
int i, nPoints;
__int16 colorIdx;
POINT_INFO pts[4];
HWR_TexSource(0);
HWR_EnableColorKey(false);
for( i = 0; i < number; ++i ) {
vtx0 = &PhdVBuf[*ptrObj++];
vtx1 = &PhdVBuf[*ptrObj++];
vtx2 = &PhdVBuf[*ptrObj++];
vtx3 = &PhdVBuf[*ptrObj++];
colorIdx = *ptrObj++;
nPoints = 4;
clipOR = LOBYTE(vtx0->clip | vtx1->clip | vtx2->clip | vtx3->clip);
clipAND = LOBYTE(vtx0->clip & vtx1->clip & vtx2->clip & vtx3->clip);
if( clipAND != 0 )
continue;
if( clipOR >= 0 ) {
if( !VBUF_VISIBLE(*vtx0, *vtx1, *vtx2) )
continue;
VBuffer[0].x = vtx0->xs;
VBuffer[0].y = vtx0->ys;
VBuffer[0].rhw = vtx0->rhw;
VBuffer[0].g = (float)vtx0->g;
VBuffer[1].x = vtx1->xs;
VBuffer[1].y = vtx1->ys;
VBuffer[1].rhw = vtx1->rhw;
VBuffer[1].g = (float)vtx1->g;
VBuffer[2].x = vtx2->xs;
VBuffer[2].y = vtx2->ys;
VBuffer[2].rhw = vtx2->rhw;
VBuffer[2].g = (float)vtx2->g;
VBuffer[3].x = vtx3->xs;
VBuffer[3].y = vtx3->ys;
VBuffer[3].rhw = vtx3->rhw;
VBuffer[3].g = (float)vtx3->g;
if( clipOR > 0 ) {
nPoints = XYGClipper(nPoints, VBuffer);
}
} else {
if( !visible_zclip(vtx0, vtx1, vtx2) )
continue;
pts[0].xv = vtx0->xv;
pts[0].yv = vtx0->yv;
pts[0].zv = vtx0->zv;
pts[0].rhw = vtx0->rhw;
pts[0].xs = vtx0->xs;
pts[0].ys = vtx0->ys;
pts[0].g = (float)vtx0->g;
pts[1].xv = vtx1->xv;
pts[1].yv = vtx1->yv;
pts[1].zv = vtx1->zv;
pts[1].rhw = vtx1->rhw;
pts[1].xs = vtx1->xs;
pts[1].ys = vtx1->ys;
pts[1].g = (float)vtx1->g;
pts[2].xv = vtx2->xv;
pts[2].yv = vtx2->yv;
pts[2].zv = vtx2->zv;
pts[2].rhw = vtx2->rhw;
pts[2].xs = vtx2->xs;
pts[2].ys = vtx2->ys;
pts[2].g = (float)vtx2->g;
pts[3].xv = vtx3->xv;
pts[3].yv = vtx3->yv;
pts[3].zv = vtx3->zv;
pts[3].rhw = vtx3->rhw;
pts[3].xs = vtx3->xs;
pts[3].ys = vtx3->ys;
pts[3].g = (float)vtx3->g;
nPoints = ZedClipper(nPoints, pts, VBuffer);
if( nPoints == 0 ) continue;
nPoints = XYGClipper(nPoints, VBuffer);
}
if( nPoints != 0 ) {
PALETTEENTRY *color = &GamePalette16[colorIdx >> 8];
#ifdef FEATURE_VIDEOFX_IMPROVED
if( AlphaBlendMode && color->peFlags > 0 && color->peFlags <= 4 ) {
float zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv, vtx3->zv);
short blend[4] = {POLY_HWR_half, POLY_HWR_add, POLY_HWR_sub, POLY_HWR_qrt};
InsertPoly_Gouraud(nPoints, zv, color->peRed, color->peGreen, color->peBlue, blend[color->peFlags - 1]);
} else {
DrawPoly_Gouraud(nPoints, color->peRed, color->peGreen, color->peBlue);
}
#else // FEATURE_VIDEOFX_IMPROVED
DrawPoly_Gouraud(nPoints, color->peRed, color->peGreen, color->peBlue);
#endif // FEATURE_VIDEOFX_IMPROVED
}
}
return ptrObj;
}
void __cdecl DrawPoly_Gouraud(int vtxCount, int red, int green, int blue) {
D3DCOLOR color;
if( vtxCount == 0 )
return;
for( int i = 0; i < vtxCount; ++i ) {
color = shadeColor(red, green, blue, 0xFF, (DWORD)VBuffer[i].g, false);
VBufferD3D[i].sx = VBuffer[i].x;
VBufferD3D[i].sy = VBuffer[i].y;
VBufferD3D[i].sz = FltResZBuf - FltResZORhw * VBuffer[i].rhw;
VBufferD3D[i].rhw = VBuffer[i].rhw;
VBufferD3D[i].color = color;
}
HWR_DrawPrimitive(D3DPT_TRIANGLEFAN, VBufferD3D, vtxCount, true);
}
__int16 *__cdecl InsertObjectG3_ZBuffered(__int16 *ptrObj, int number, SORTTYPE sortType) {
char clipOR, clipAND;
PHD_VBUF *vtx0, *vtx1, *vtx2;
int i, nPoints;
__int16 colorIdx;
POINT_INFO pts[3];
HWR_TexSource(0);
HWR_EnableColorKey(false);
for( i = 0; i < number; ++i ) {
vtx0 = &PhdVBuf[*ptrObj++];
vtx1 = &PhdVBuf[*ptrObj++];
vtx2 = &PhdVBuf[*ptrObj++];
colorIdx = *ptrObj++;
nPoints = 3;
clipOR = LOBYTE(vtx0->clip | vtx1->clip | vtx2->clip);
clipAND = LOBYTE(vtx0->clip & vtx1->clip & vtx2->clip);
if( clipAND != 0 )
continue;
if( clipOR >= 0 ) {
if( !VBUF_VISIBLE(*vtx0, *vtx1, *vtx2) )
continue;
VBuffer[0].x = vtx0->xs;
VBuffer[0].y = vtx0->ys;
VBuffer[0].rhw = vtx0->rhw;
VBuffer[0].g = (float)vtx0->g;
VBuffer[1].x = vtx1->xs;
VBuffer[1].y = vtx1->ys;
VBuffer[1].rhw = vtx1->rhw;
VBuffer[1].g = (float)vtx1->g;
VBuffer[2].x = vtx2->xs;
VBuffer[2].y = vtx2->ys;
VBuffer[2].rhw = vtx2->rhw;
VBuffer[2].g = (float)vtx2->g;
if( clipOR > 0 ) {
nPoints = XYGClipper(nPoints, VBuffer);
}
} else {
if( !visible_zclip(vtx0, vtx1, vtx2) )
continue;
pts[0].xv = vtx0->xv;
pts[0].yv = vtx0->yv;
pts[0].zv = vtx0->zv;
pts[0].rhw = vtx0->rhw;
pts[0].xs = vtx0->xs;
pts[0].ys = vtx0->ys;
pts[0].g = (float)vtx0->g;
pts[1].xv = vtx1->xv;
pts[1].yv = vtx1->yv;
pts[1].zv = vtx1->zv;
pts[1].rhw = vtx1->rhw;
pts[1].xs = vtx1->xs;
pts[1].ys = vtx1->ys;
pts[1].g = (float)vtx1->g;
pts[2].xv = vtx2->xv;
pts[2].yv = vtx2->yv;
pts[2].zv = vtx2->zv;
pts[2].rhw = vtx2->rhw;
pts[2].xs = vtx2->xs;
pts[2].ys = vtx2->ys;
pts[2].g = (float)vtx2->g;
nPoints = ZedClipper(nPoints, pts, VBuffer);
if( nPoints == 0 ) continue;
nPoints = XYGClipper(nPoints, VBuffer);
}
if( nPoints != 0 ) {
PALETTEENTRY *color = &GamePalette16[colorIdx >> 8];
#ifdef FEATURE_VIDEOFX_IMPROVED
if( AlphaBlendMode && color->peFlags > 0 && color->peFlags <= 4 ) {
float zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv);
short blend[4] = {POLY_HWR_half, POLY_HWR_add, POLY_HWR_sub, POLY_HWR_qrt};
InsertPoly_Gouraud(nPoints, zv, color->peRed, color->peGreen, color->peBlue, blend[color->peFlags - 1]);
} else {
DrawPoly_Gouraud(nPoints, color->peRed, color->peGreen, color->peBlue);
}
#else // FEATURE_VIDEOFX_IMPROVED
DrawPoly_Gouraud(nPoints, color->peRed, color->peGreen, color->peBlue);
#endif // FEATURE_VIDEOFX_IMPROVED
}
}
return ptrObj;
}
void __cdecl InsertFlatRect_ZBuffered(int x0, int y0, int x1, int y1, int z, BYTE colorIdx) {
double sz, rhw;
D3DCOLOR color;
if( x0 >= x1 || y0 >= y1 )
return;
if( x0 < PhdWinMinX )
x0 = PhdWinMinX;
if( y0 < PhdWinMinY )
y0 = PhdWinMinY;
if( x1 > PhdWinMinX + PhdWinWidth )
x1 = PhdWinMinX + PhdWinWidth;
if( y1 > PhdWinMinY + PhdWinHeight )
x1 = PhdWinMinY + PhdWinHeight;
CLAMP(z, PhdNearZ, PhdFarZ);
color = shadeColor(GamePalette8[colorIdx].red, GamePalette8[colorIdx].green, GamePalette8[colorIdx].blue, 0xFF, 0, false);
rhw = RhwFactor / (double)z;
sz = FltResZBuf - rhw * FltResZORhw;
VBufferD3D[0].sx = (float)x0;
VBufferD3D[0].sy = (float)y0;
VBufferD3D[1].sx = (float)x1;
VBufferD3D[1].sy = (float)y0;
VBufferD3D[2].sx = (float)x0;
VBufferD3D[2].sy = (float)y1;
VBufferD3D[3].sx = (float)x1;
VBufferD3D[3].sy = (float)y1;
for( int i=0; i<4; ++i ) {
VBufferD3D[i].sz = sz;
VBufferD3D[i].rhw = rhw;
VBufferD3D[i].color = color;
}
HWR_TexSource(0);
HWR_EnableColorKey(false);
HWR_DrawPrimitive(D3DPT_TRIANGLESTRIP, VBufferD3D, 4, true);
}
void __cdecl InsertLine_ZBuffered(int x0, int y0, int x1, int y1, int z, BYTE colorIdx) {
double sz, rhw;
D3DCOLOR color;
if( z > PhdFarZ ) {
return;
}
if( z < PhdNearZ ) {
z = PhdNearZ;
}
color = shadeColor(GamePalette8[colorIdx].red, GamePalette8[colorIdx].green, GamePalette8[colorIdx].blue, 0xFF, 0, false);
rhw = RhwFactor / (double)z;
sz = FltResZBuf - rhw * FltResZORhw;
VBufferD3D[0].sx = (float)(PhdWinMinX + x0);
VBufferD3D[0].sy = (float)(PhdWinMinY + y0);
VBufferD3D[1].sx = (float)(PhdWinMinX + x1);
VBufferD3D[1].sy = (float)(PhdWinMinY + y1);
for( int i=0; i<2; ++i ) {
VBufferD3D[i].sz = sz;
VBufferD3D[i].rhw = rhw;
VBufferD3D[i].color = color;
}
HWR_TexSource(0);
HWR_EnableColorKey(false);
HWR_DrawPrimitive(D3DPT_LINESTRIP, VBufferD3D, 2, true);
}
void __cdecl InsertGT3_Sorted(PHD_VBUF *vtx0, PHD_VBUF *vtx1, PHD_VBUF *vtx2, PHD_TEXTURE *texture, PHD_UV *uv0, PHD_UV *uv1, PHD_UV *uv2, SORTTYPE sortType) {
char clipOR, clipAND;
float zv;
POINT_INFO points[3];
int nPoints = 3;
clipOR = LOBYTE(vtx0->clip | vtx1->clip | vtx2->clip);
clipAND = LOBYTE(vtx0->clip & vtx1->clip & vtx2->clip);
if( clipAND != 0 )
return;
if( clipOR >= 0 ) {
if( !VBUF_VISIBLE(*vtx0, *vtx1, *vtx2) )
return;
if( clipOR == 0 ) {
zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv);
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(zv);
++Sort3dPtr;
#ifdef FEATURE_VIDEOFX_IMPROVED
*Info3dPtr++ = GetPolyType(texture->drawtype);
#else // FEATURE_VIDEOFX_IMPROVED
*Info3dPtr++ = ( texture->drawtype == DRAW_Opaque ) ? POLY_HWR_GTmap : POLY_HWR_WGTmap;
#endif // FEATURE_VIDEOFX_IMPROVED
*Info3dPtr++ = texture->tpage;
*Info3dPtr++ = 3;
*(D3DTLVERTEX **)Info3dPtr = HWR_VertexPtr;
Info3dPtr += sizeof(D3DTLVERTEX *)/sizeof(__int16);
HWR_VertexPtr[0].sx = vtx0->xs;
HWR_VertexPtr[0].sy = vtx0->ys;
HWR_VertexPtr[0].sz = FltResZBuf - FltResZORhw * vtx0->rhw; // NOTE: there was bug because of uninitialized sz and rhw
HWR_VertexPtr[0].rhw = vtx0->rhw;
HWR_VertexPtr[0].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx0->g, true);
HWR_VertexPtr[0].tu = (double)uv0->u / (double)PHD_ONE;
HWR_VertexPtr[0].tv = (double)uv0->v / (double)PHD_ONE;
HWR_VertexPtr[1].sx = vtx1->xs;
HWR_VertexPtr[1].sy = vtx1->ys;
HWR_VertexPtr[1].sz = FltResZBuf - FltResZORhw * vtx1->rhw; // NOTE: there was bug because of uninitialized sz and rhw
HWR_VertexPtr[1].rhw = vtx1->rhw;
HWR_VertexPtr[1].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx1->g, true);
HWR_VertexPtr[1].tu = (double)uv1->u / (double)PHD_ONE;
HWR_VertexPtr[1].tv = (double)uv1->v / (double)PHD_ONE;
HWR_VertexPtr[2].sx = vtx2->xs;
HWR_VertexPtr[2].sy = vtx2->ys;
HWR_VertexPtr[2].sz = FltResZBuf - FltResZORhw * vtx2->rhw; // NOTE: there was bug because of uninitialized sz and rhw
HWR_VertexPtr[2].rhw = vtx2->rhw;
HWR_VertexPtr[2].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx2->g, true);
HWR_VertexPtr[2].tu = (double)uv2->u / (double)PHD_ONE;
HWR_VertexPtr[2].tv = (double)uv2->v / (double)PHD_ONE;
HWR_VertexPtr += 3;
++SurfaceCount;
return;
}
VBuffer[0].x = vtx0->xs;
VBuffer[0].y = vtx0->ys;
VBuffer[0].rhw = vtx0->rhw;
VBuffer[0].g = (double)vtx0->g;
VBuffer[0].u = (double)uv0->u * vtx0->rhw;
VBuffer[0].v = (double)uv0->v * vtx0->rhw;
VBuffer[1].x = vtx1->xs;
VBuffer[1].y = vtx1->ys;
VBuffer[1].rhw = vtx1->rhw;
VBuffer[1].g = (double)vtx1->g;
VBuffer[1].u = (double)uv1->u * vtx1->rhw;
VBuffer[1].v = (double)uv1->v * vtx1->rhw;
VBuffer[2].x = vtx2->xs;
VBuffer[2].y = vtx2->ys;
VBuffer[2].rhw = vtx2->rhw;
VBuffer[2].g = (double)vtx2->g;
VBuffer[2].u = (double)uv2->u * vtx2->rhw;
VBuffer[2].v = (double)uv2->v * vtx2->rhw;
} else {
if( !visible_zclip(vtx0, vtx1, vtx2) )
return;
points[0].xv = vtx0->xv;
points[0].yv = vtx0->yv;
points[0].zv = vtx0->zv;
points[0].rhw = vtx0->rhw;
points[0].xs = vtx0->xs;
points[0].ys = vtx0->ys;
points[0].u = (float)uv0->u;
points[0].v = (float)uv0->v;
points[0].g = (float)vtx0->g;
points[1].yv = vtx1->yv;
points[1].xv = vtx1->xv;
points[1].zv = vtx1->zv;
points[1].rhw = vtx1->rhw;
points[1].xs = vtx1->xs;
points[1].ys = vtx1->ys;
points[1].u = (float)uv1->u;
points[1].v = (float)uv1->v;
points[1].g = (float)vtx1->g;
points[2].xv = vtx2->xv;
points[2].yv = vtx2->yv;
points[2].zv = vtx2->zv;
points[2].rhw = vtx2->rhw;
points[2].xs = vtx2->xs;
points[2].ys = vtx2->ys;
points[2].u = (float)uv2->u;
points[2].v = (float)uv2->v;
points[2].g = (float)vtx2->g;
nPoints = ZedClipper(nPoints, points, VBuffer);
if( nPoints == 0 ) return;
}
nPoints = XYGUVClipper(nPoints, VBuffer);
if( nPoints == 0 ) return;
zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv);
#ifdef FEATURE_VIDEOFX_IMPROVED
InsertClippedPoly_Textured(nPoints, zv, GetPolyType(texture->drawtype), texture->tpage);
#else // FEATURE_VIDEOFX_IMPROVED
InsertClippedPoly_Textured(nPoints, zv, ( texture->drawtype == DRAW_Opaque ) ? POLY_HWR_GTmap : POLY_HWR_WGTmap, texture->tpage);
#endif // FEATURE_VIDEOFX_IMPROVED
}
void __cdecl InsertClippedPoly_Textured(int vtxCount, float z, __int16 polyType, __int16 texPage) {
double tu, tv;
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(z);
++Sort3dPtr;
*(Info3dPtr++) = polyType;
*(Info3dPtr++) = texPage;
*(Info3dPtr++) = vtxCount;
*(D3DTLVERTEX **)Info3dPtr = HWR_VertexPtr;
Info3dPtr += sizeof(D3DTLVERTEX *)/sizeof(__int16);
for( int i = 0; i < vtxCount; ++i ) {
tu = VBuffer[i].u / double(PHD_ONE) / VBuffer[i].rhw;
tv = VBuffer[i].v / double(PHD_ONE) / VBuffer[i].rhw;
CLAMP(tu, 0.0, 1.0);
CLAMP(tv, 0.0, 1.0);
HWR_VertexPtr[i].sx = VBuffer[i].x;
HWR_VertexPtr[i].sy = VBuffer[i].y;
HWR_VertexPtr[i].sz = FltResZBuf - FltResZORhw * VBuffer[i].rhw; // NOTE: there was bug because of uninitialized sz and rhw
HWR_VertexPtr[i].rhw = VBuffer[i].rhw;
HWR_VertexPtr[i].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, VBuffer[i].g, true);
HWR_VertexPtr[i].tu = tu;
HWR_VertexPtr[i].tv = tv;
}
HWR_VertexPtr += vtxCount;
++SurfaceCount;
}
void __cdecl InsertGT4_Sorted(PHD_VBUF *vtx0, PHD_VBUF *vtx1, PHD_VBUF *vtx2, PHD_VBUF *vtx3, PHD_TEXTURE *texture, SORTTYPE sortType) {
char clipOR, clipAND;
float zv;
clipOR = LOBYTE(vtx0->clip | vtx1->clip | vtx2->clip | vtx3->clip);
clipAND = LOBYTE(vtx0->clip & vtx1->clip & vtx2->clip & vtx3->clip);
if( clipAND != 0 )
return;
if( clipOR == 0 && VBUF_VISIBLE(*vtx0, *vtx1, *vtx2) ) {
zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv, vtx3->zv);
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(zv);
++Sort3dPtr;
#ifdef FEATURE_VIDEOFX_IMPROVED
*Info3dPtr++ = GetPolyType(texture->drawtype);
#else // FEATURE_VIDEOFX_IMPROVED
*Info3dPtr++ = ( texture->drawtype == DRAW_Opaque ) ? POLY_HWR_GTmap : POLY_HWR_WGTmap;
#endif // FEATURE_VIDEOFX_IMPROVED
*Info3dPtr++ = texture->tpage;
*Info3dPtr++ = 4;
*(D3DTLVERTEX **)Info3dPtr = HWR_VertexPtr;
Info3dPtr += sizeof(D3DTLVERTEX *)/sizeof(__int16);
HWR_VertexPtr[0].sx = vtx0->xs;
HWR_VertexPtr[0].sy = vtx0->ys;
HWR_VertexPtr[0].sz = FltResZBuf - FltResZORhw * vtx0->rhw; // NOTE: there was bug because of uninitialized sz and rhw
HWR_VertexPtr[0].rhw = vtx0->rhw;
HWR_VertexPtr[0].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx0->g, true);
HWR_VertexPtr[0].tu = (double)texture->uv[0].u / (double)PHD_ONE;
HWR_VertexPtr[0].tv = (double)texture->uv[0].v / (double)PHD_ONE;
HWR_VertexPtr[1].sx = vtx1->xs;
HWR_VertexPtr[1].sy = vtx1->ys;
HWR_VertexPtr[1].sz = FltResZBuf - FltResZORhw * vtx1->rhw; // NOTE: there was bug because of uninitialized sz and rhw
HWR_VertexPtr[1].rhw = vtx1->rhw;
HWR_VertexPtr[1].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx1->g, true);
HWR_VertexPtr[1].tu = (double)texture->uv[1].u / (double)PHD_ONE;
HWR_VertexPtr[1].tv = (double)texture->uv[1].v / (double)PHD_ONE;
HWR_VertexPtr[2].sx = vtx2->xs;
HWR_VertexPtr[2].sy = vtx2->ys;
HWR_VertexPtr[2].sz = FltResZBuf - FltResZORhw * vtx2->rhw; // NOTE: there was bug because of uninitialized sz and rhw
HWR_VertexPtr[2].rhw = vtx2->rhw;
HWR_VertexPtr[2].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx2->g, true);
HWR_VertexPtr[2].tu = (double)texture->uv[2].u / (double)PHD_ONE;
HWR_VertexPtr[2].tv = (double)texture->uv[2].v / (double)PHD_ONE;
HWR_VertexPtr[3].sx = vtx3->xs;
HWR_VertexPtr[3].sy = vtx3->ys;
HWR_VertexPtr[3].sz = FltResZBuf - FltResZORhw * vtx3->rhw; // NOTE: there was bug because of uninitialized sz and rhw
HWR_VertexPtr[3].rhw = vtx3->rhw;
HWR_VertexPtr[3].color = shadeColor(0xFF, 0xFF, 0xFF, 0xFF, vtx3->g, true);
HWR_VertexPtr[3].tu = (double)texture->uv[3].u / (double)PHD_ONE;
HWR_VertexPtr[3].tv = (double)texture->uv[3].v / (double)PHD_ONE;
HWR_VertexPtr += 4;
++SurfaceCount;
}
else if( (clipOR < 0 && visible_zclip(vtx0, vtx1, vtx2)) ||
(clipOR > 0 && VBUF_VISIBLE(*vtx0, *vtx1, *vtx2)) )
{
InsertGT3_Sorted(vtx0, vtx1, vtx2, texture, texture->uv, &texture->uv[1], &texture->uv[2], sortType);
InsertGT3_Sorted(vtx0, vtx2, vtx3, texture, texture->uv, &texture->uv[2], &texture->uv[3], sortType);
}
}
__int16 *__cdecl InsertObjectGT4_Sorted(__int16 *ptrObj, int number, SORTTYPE sortType) {
PHD_VBUF *vtx0, *vtx1, *vtx2, *vtx3;
PHD_TEXTURE *texture;
for( int i = 0; i < number; ++i ) {
if( HWR_VertexBufferFull() ) {
ptrObj += (number - i) * 5;
break;
}
vtx0 = &PhdVBuf[ptrObj[0]];
vtx1 = &PhdVBuf[ptrObj[1]];
vtx2 = &PhdVBuf[ptrObj[2]];
vtx3 = &PhdVBuf[ptrObj[3]];
texture = &PhdTextureInfo[ptrObj[4]];
ptrObj += 5;
InsertGT4_Sorted(vtx0, vtx1, vtx2, vtx3, texture, sortType);
}
return ptrObj;
}
__int16 *__cdecl InsertObjectGT3_Sorted(__int16 *ptrObj, int number, SORTTYPE sortType) {
PHD_VBUF *vtx0, *vtx1, *vtx2;
PHD_TEXTURE *texture;
PHD_UV *uv;
for( int i = 0; i < number; ++i ) {
if( HWR_VertexBufferFull() ) {
ptrObj += (number - i) * 4;
break;
}
vtx0 = &PhdVBuf[ptrObj[0]];
vtx1 = &PhdVBuf[ptrObj[1]];
vtx2 = &PhdVBuf[ptrObj[2]];
texture = &PhdTextureInfo[ptrObj[3]];
uv = texture->uv;
ptrObj += 4;
InsertGT3_Sorted(vtx0, vtx1, vtx2, texture, &uv[0], &uv[1], &uv[2], sortType);
}
return ptrObj;
}
__int16 *__cdecl InsertObjectG4_Sorted(__int16 *ptrObj, int number, SORTTYPE sortType) {
char clipOR, clipAND;
PHD_VBUF *vtx0, *vtx1, *vtx2, *vtx3;
int i, nPoints;
float zv;
__int16 colorIdx;
PALETTEENTRY *color;
POINT_INFO pts[4];
for( i = 0; i < number; ++i ) {
if( HWR_VertexBufferFull() ) {
ptrObj += number - i;
break;
}
vtx0 = &PhdVBuf[*ptrObj++];
vtx1 = &PhdVBuf[*ptrObj++];
vtx2 = &PhdVBuf[*ptrObj++];
vtx3 = &PhdVBuf[*ptrObj++];
colorIdx = *ptrObj++;
nPoints = 4;
clipOR = LOBYTE(vtx0->clip | vtx1->clip | vtx2->clip | vtx3->clip);
clipAND = LOBYTE(vtx0->clip & vtx1->clip & vtx2->clip & vtx3->clip);
if( clipAND != 0 )
continue;
if( clipOR >= 0 ) {
if( !VBUF_VISIBLE(*vtx0, *vtx1, *vtx2) )
continue;
VBuffer[0].x = vtx0->xs;
VBuffer[0].y = vtx0->ys;
VBuffer[0].rhw = vtx0->rhw;
VBuffer[0].g = (float)vtx0->g;
VBuffer[1].x = vtx1->xs;
VBuffer[1].y = vtx1->ys;
VBuffer[1].rhw = vtx1->rhw;
VBuffer[1].g = (float)vtx1->g;
VBuffer[2].x = vtx2->xs;
VBuffer[2].y = vtx2->ys;
VBuffer[2].rhw = vtx2->rhw;
VBuffer[2].g = (float)vtx2->g;
VBuffer[3].x = vtx3->xs;
VBuffer[3].y = vtx3->ys;
VBuffer[3].rhw = vtx3->rhw;
VBuffer[3].g = (float)vtx3->g;
if( clipOR > 0 ) {
nPoints = XYGClipper(nPoints, VBuffer);
}
} else {
if( !visible_zclip(vtx0, vtx1, vtx2) )
continue;
pts[0].xv = vtx0->xv;
pts[0].yv = vtx0->yv;
pts[0].zv = vtx0->zv;
pts[0].rhw = vtx0->rhw;
pts[0].xs = vtx0->xs;
pts[0].ys = vtx0->ys;
pts[0].g = (float)vtx0->g;
pts[1].xv = vtx1->xv;
pts[1].yv = vtx1->yv;
pts[1].zv = vtx1->zv;
pts[1].rhw = vtx1->rhw;
pts[1].xs = vtx1->xs;
pts[1].ys = vtx1->ys;
pts[1].g = (float)vtx1->g;
pts[2].xv = vtx2->xv;
pts[2].yv = vtx2->yv;
pts[2].zv = vtx2->zv;
pts[2].rhw = vtx2->rhw;
pts[2].xs = vtx2->xs;
pts[2].ys = vtx2->ys;
pts[2].g = (float)vtx2->g;
pts[3].xv = vtx3->xv;
pts[3].yv = vtx3->yv;
pts[3].zv = vtx3->zv;
pts[3].rhw = vtx3->rhw;
pts[3].xs = vtx3->xs;
pts[3].ys = vtx3->ys;
pts[3].g = (float)vtx3->g;
nPoints = ZedClipper(nPoints, pts, VBuffer);
if( nPoints == 0 ) continue;
nPoints = XYGClipper(nPoints, VBuffer);
}
if( nPoints == 0 )
continue;
color = &GamePalette16[colorIdx >> 8];
zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv, vtx3->zv);
#ifdef FEATURE_VIDEOFX_IMPROVED
if( AlphaBlendMode && color->peFlags > 0 && color->peFlags <= 4 ) {
short blend[4] = {POLY_HWR_half, POLY_HWR_add, POLY_HWR_sub, POLY_HWR_qrt};
InsertPoly_Gouraud(nPoints, zv, color->peRed, color->peGreen, color->peBlue, blend[color->peFlags - 1]);
} else {
InsertPoly_Gouraud(nPoints, zv, color->peRed, color->peGreen, color->peBlue, POLY_HWR_gouraud);
}
#else // FEATURE_VIDEOFX_IMPROVED
InsertPoly_Gouraud(nPoints, zv, color->peRed, color->peGreen, color->peBlue, POLY_HWR_gouraud);
#endif // FEATURE_VIDEOFX_IMPROVED
}
return ptrObj;
}
void __cdecl InsertPoly_Gouraud(int vtxCount, float z, int red, int green, int blue, __int16 polyType) {
BYTE alpha = ( polyType == POLY_HWR_trans ) ? 0x80 : 0xFF;
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(z);
++Sort3dPtr;
*(Info3dPtr++) = polyType;
*(Info3dPtr++) = vtxCount;
*(D3DTLVERTEX **)Info3dPtr = HWR_VertexPtr;
Info3dPtr += sizeof(D3DTLVERTEX *)/sizeof(__int16);
for( int i = 0; i < vtxCount; ++i ) {
HWR_VertexPtr[i].sx = VBuffer[i].x;
HWR_VertexPtr[i].sy = VBuffer[i].y;
HWR_VertexPtr[i].sz = FltResZBuf - FltResZORhw * VBuffer[i].rhw; // NOTE: there was bug because of uninitialized sz and rhw
HWR_VertexPtr[i].rhw = VBuffer[i].rhw;
HWR_VertexPtr[i].color = shadeColor(red, green, blue, alpha, VBuffer[i].g, false);
}
HWR_VertexPtr += vtxCount;
++SurfaceCount;
}
__int16 *__cdecl InsertObjectG3_Sorted(__int16 *ptrObj, int number, SORTTYPE sortType) {
char clipOR, clipAND;
PHD_VBUF *vtx0, *vtx1, *vtx2;
int i, nPoints;
float zv;
__int16 colorIdx;
PALETTEENTRY *color;
POINT_INFO pts[3];
for( i = 0; i < number; ++i ) {
if( HWR_VertexBufferFull() ) {
ptrObj += number - i;
break;
}
vtx0 = &PhdVBuf[*ptrObj++];
vtx1 = &PhdVBuf[*ptrObj++];
vtx2 = &PhdVBuf[*ptrObj++];
colorIdx = *ptrObj++;
nPoints = 3;
clipOR = LOBYTE(vtx0->clip | vtx1->clip | vtx2->clip);
clipAND = LOBYTE(vtx0->clip & vtx1->clip & vtx2->clip);
if( clipAND != 0 )
continue;
if( clipOR >= 0 ) {
if( !VBUF_VISIBLE(*vtx0, *vtx1, *vtx2) )
continue;
VBuffer[0].x = vtx0->xs;
VBuffer[0].y = vtx0->ys;
VBuffer[0].rhw = vtx0->rhw;
VBuffer[0].g = (float)vtx0->g;
VBuffer[1].x = vtx1->xs;
VBuffer[1].y = vtx1->ys;
VBuffer[1].rhw = vtx1->rhw;
VBuffer[1].g = (float)vtx1->g;
VBuffer[2].x = vtx2->xs;
VBuffer[2].y = vtx2->ys;
VBuffer[2].rhw = vtx2->rhw;
VBuffer[2].g = (float)vtx2->g;
if( clipOR > 0 ) {
nPoints = XYGClipper(nPoints, VBuffer);
}
} else {
if( !visible_zclip(vtx0, vtx1, vtx2) )
continue;
pts[0].xv = vtx0->xv;
pts[0].yv = vtx0->yv;
pts[0].zv = vtx0->zv;
pts[0].rhw = vtx0->rhw;
pts[0].xs = vtx0->xs;
pts[0].ys = vtx0->ys;
pts[0].g = (float)vtx0->g;
pts[1].xv = vtx1->xv;
pts[1].yv = vtx1->yv;
pts[1].zv = vtx1->zv;
pts[1].rhw = vtx1->rhw;
pts[1].xs = vtx1->xs;
pts[1].ys = vtx1->ys;
pts[1].g = (float)vtx1->g;
pts[2].xv = vtx2->xv;
pts[2].yv = vtx2->yv;
pts[2].zv = vtx2->zv;
pts[2].rhw = vtx2->rhw;
pts[2].xs = vtx2->xs;
pts[2].ys = vtx2->ys;
pts[2].g = (float)vtx2->g;
nPoints = ZedClipper(nPoints, pts, VBuffer);
if( nPoints == 0 ) continue;
nPoints = XYGClipper(nPoints, VBuffer);
}
if( nPoints == 0 )
continue;
color = &GamePalette16[colorIdx >> 8];
zv = CalculatePolyZ(sortType, vtx0->zv, vtx1->zv, vtx2->zv);
#ifdef FEATURE_VIDEOFX_IMPROVED
if( AlphaBlendMode && color->peFlags > 0 && color->peFlags <= 4 ) {
short blend[4] = {POLY_HWR_half, POLY_HWR_add, POLY_HWR_sub, POLY_HWR_qrt};
InsertPoly_Gouraud(nPoints, zv, color->peRed, color->peGreen, color->peBlue, blend[color->peFlags - 1]);
} else {
InsertPoly_Gouraud(nPoints, zv, color->peRed, color->peGreen, color->peBlue, POLY_HWR_gouraud);
}
#else // FEATURE_VIDEOFX_IMPROVED
InsertPoly_Gouraud(nPoints, zv, color->peRed, color->peGreen, color->peBlue, POLY_HWR_gouraud);
#endif // FEATURE_VIDEOFX_IMPROVED
}
return ptrObj;
}
#ifdef FEATURE_VIDEOFX_IMPROVED
void __cdecl InsertSprite_Sorted(int z, int x0, int y0, int x1, int y1, int spriteIdx, __int16 shade, DWORD flags) {
if( TextureFormat.bpp < 16 && CHK_ANY(flags, SPR_TINT) ) return; // tinted sprites are not supported for 8 bit textured mode
#else // FEATURE_VIDEOFX_IMPROVED
void __cdecl InsertSprite_Sorted(int z, int x0, int y0, int x1, int y1, int spriteIdx, __int16 shade) {
#endif // FEATURE_VIDEOFX_IMPROVED
double rhw, u0, v0, u1, v1;
int uOffset, vOffset, nPoints;
if( HWR_VertexBufferFull() || x0 >= x1 || y0 >= y1 || x1 <= 0 || y1 <= 0 || x0 >= PhdWinMaxX || y0 >= PhdWinMaxY || z >= PhdFarZ )
return;
x0 += PhdWinMinX;
y0 += PhdWinMinY;
x1 += PhdWinMinX;
y1 += PhdWinMinY;
if( z < PhdNearZ )
z = PhdNearZ;
rhw = RhwFactor / (double)z;
uOffset = LOBYTE(PhdSpriteInfo[spriteIdx].offset) * 256;
vOffset = HIBYTE(PhdSpriteInfo[spriteIdx].offset) * 256;
// NOTE: page side is not counted in the original game, but we need it for HD textures
int adjustment = UvAdd * 256 / GetTextureSideByPage(PhdSpriteInfo[spriteIdx].texPage);
#if (DIRECT3D_VERSION >= 0x900)
double forcedAdjust = GetTexPagesAdjustment();
if( forcedAdjust > 0.0) {
adjustment = (int)(forcedAdjust * 256.0);
}
#endif // (DIRECT3D_VERSION >= 0x900)
#ifdef FEATURE_HUD_IMPROVED
if( spriteIdx >= (int)ARRAY_SIZE(PhdSpriteInfo) - HUD_SPRITE_RESERVED ) {
adjustment = 0;
}
#endif // FEATURE_HUD_IMPROVED
CLAMPL(adjustment, 1);
u0 = rhw * (double)(uOffset - adjustment + PhdSpriteInfo[spriteIdx].width);
v0 = rhw * (double)(vOffset + adjustment);
u1 = rhw * (double)(uOffset + adjustment);
v1 = rhw * (double)(vOffset - adjustment + PhdSpriteInfo[spriteIdx].height);
VBuffer[0].x = (float)x0;
VBuffer[0].y = (float)y0;
VBuffer[0].u = u1;
VBuffer[0].v = v0;
VBuffer[1].x = (float)x1;
VBuffer[1].y = (float)y0;
VBuffer[1].u = u0;
VBuffer[1].v = v0;
VBuffer[2].x = (float)x1;
VBuffer[2].y = (float)y1;
VBuffer[2].u = u0;
VBuffer[2].v = v1;
VBuffer[3].x = (float)x0;
VBuffer[3].y = (float)y1;
VBuffer[3].u = u1;
VBuffer[3].v = v1;
for( int i=0; i<4; ++i ) {
VBuffer[i].rhw = rhw;
VBuffer[i].g = (float)shade;
}
nPoints = 4;
if( x0 < PhdWinMinX || y0 < PhdWinMinY || x1 > PhdWinWidth + PhdWinMinX || y1 > PhdWinHeight + PhdWinMinY ) {
FltWinLeft = (float)PhdWinMinX;
FltWinTop = (float)PhdWinMinY;
FltWinRight = (float)(PhdWinMinX + PhdWinWidth);
FltWinBottom = (float)(PhdWinMinY + PhdWinHeight);
nPoints = XYGUVClipper(nPoints, VBuffer);
if( nPoints == 0 ) return;
}
bool isShadeEffectBackup = IsShadeEffect;
IsShadeEffect = false;
#ifdef FEATURE_VIDEOFX_IMPROVED
short polyType = POLY_HWR_WGTmap;
if( CHK_ANY(flags, SPR_TINT) ) {
GlobalTint = RGBA_SETALPHA(flags, 0xFF);
}
if( AlphaBlendMode && CHK_ANY(flags, SPR_SEMITRANS) ) {
short blend[4] = {
POLY_HWR_WGTmapHalf,
POLY_HWR_WGTmapAdd,
POLY_HWR_WGTmapSub,
POLY_HWR_WGTmapQrt,
};
polyType = blend[(flags & SPR_BLEND) >> 29];
}
InsertClippedPoly_Textured(nPoints, (float)z, polyType, PhdSpriteInfo[spriteIdx].texPage);
GlobalTint = 0;
#else // FEATURE_VIDEOFX_IMPROVED
InsertClippedPoly_Textured(nPoints, (float)z, POLY_HWR_WGTmap, PhdSpriteInfo[spriteIdx].texPage);
#endif // FEATURE_VIDEOFX_IMPROVED
IsShadeEffect = isShadeEffectBackup;
}
void __cdecl InsertFlatRect_Sorted(int x0, int y0, int x1, int y1, int z, BYTE colorIdx) {
double rhw, sz;
D3DCOLOR color;
if( x0 >= x1 || y0 >= y1 )
return;
if( x0 < PhdWinMinX )
x0 = PhdWinMinX;
if( y0 < PhdWinMinY )
y0 = PhdWinMinY;
if( x1 > PhdWinMinX + PhdWinWidth )
x1 = PhdWinMinX + PhdWinWidth;
if( y1 > PhdWinMinY + PhdWinHeight )
x1 = PhdWinMinY + PhdWinHeight;
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(z);
++Sort3dPtr;
*(Info3dPtr++) = POLY_HWR_gouraud;
*(Info3dPtr++) = 4; // vertex count
*(D3DTLVERTEX **)Info3dPtr = HWR_VertexPtr;
Info3dPtr += sizeof(D3DTLVERTEX *)/sizeof(__int16);
color = shadeColor(GamePalette8[colorIdx].red, GamePalette8[colorIdx].green, GamePalette8[colorIdx].blue, 0xFF, 0, false);
rhw = RhwFactor / (double)z;
sz = FltResZBuf - rhw * FltResZORhw;
HWR_VertexPtr[0].sx = (float)x0;
HWR_VertexPtr[0].sy = (float)y0;
HWR_VertexPtr[1].sx = (float)x1;
HWR_VertexPtr[1].sy = (float)y0;
HWR_VertexPtr[2].sx = (float)x1;
HWR_VertexPtr[2].sy = (float)y1;
HWR_VertexPtr[3].sx = (float)x0;
HWR_VertexPtr[3].sy = (float)y1;
for( int i=0; i<4; ++i ) {
HWR_VertexPtr[i].color = color;
HWR_VertexPtr[i].sz = sz; // NOTE: there was bug because of uninitialized sz and rhw
HWR_VertexPtr[i].rhw = rhw;
}
HWR_VertexPtr += 4;
++SurfaceCount;
}
void __cdecl InsertLine_Sorted(int x0, int y0, int x1, int y1, int z, BYTE colorIdx) {
double rhw, sz;
D3DCOLOR color;
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(z);
++Sort3dPtr;
*(Info3dPtr++) = POLY_HWR_line;
*(Info3dPtr++) = 2; // vertex count
*(D3DTLVERTEX **)Info3dPtr = HWR_VertexPtr;
Info3dPtr += sizeof(D3DTLVERTEX *)/sizeof(__int16);
color = shadeColor(GamePalette8[colorIdx].red, GamePalette8[colorIdx].green, GamePalette8[colorIdx].blue, 0xFF, 0, false);
rhw = RhwFactor / (double)z;
sz = FltResZBuf - rhw * FltResZORhw;
HWR_VertexPtr[0].sx = (float)(PhdWinMinX + x0);
HWR_VertexPtr[0].sy = (float)(PhdWinMinY + y0);
HWR_VertexPtr[1].sx = (float)(PhdWinMinX + x1);
HWR_VertexPtr[1].sy = (float)(PhdWinMinY + y1);
for( int i=0; i<2; ++i ) {
HWR_VertexPtr[i].color = color;
HWR_VertexPtr[i].sz = sz; // NOTE: there was bug because of uninitialized sz and rhw
HWR_VertexPtr[i].rhw = rhw;
}
HWR_VertexPtr += 2;
++SurfaceCount;
}
void __cdecl InsertTrans8_Sorted(PHD_VBUF *vbuf, __int16 shade) {
int i, nPoints, polyZ;
char clipOR = 0x00;
char clipAND = 0xFF;
#ifdef FEATURE_VIDEOFX_IMPROVED
int nVtx = ( ShadowMode == 1 ) ? 32 : 8;
#else // FEATURE_VIDEOFX_IMPROVED
int nVtx = 8;
#endif // FEATURE_VIDEOFX_IMPROVED
for( i = 0; i < nVtx; ++i ) {
clipOR |= LOBYTE(vbuf[i].clip);
clipAND &= LOBYTE(vbuf[i].clip);
}
if( (clipOR < 0) || (clipAND != 0) || !VBUF_VISIBLE(vbuf[0], vbuf[1], vbuf[2]) )
return;
for( i = 0; i < nVtx; ++i ) {
VBuffer[i].x = vbuf[i].xs;
VBuffer[i].y = vbuf[i].ys;
VBuffer[i].rhw = RhwFactor / (double)(vbuf[i].zv - 0x20000);
}
nPoints = nVtx;
if( clipOR != 0 ) {
FltWinLeft = 0.0;
FltWinTop = 0.0;
FltWinRight = (float)PhdWinMaxX;
FltWinBottom = (float)PhdWinMaxY;
nPoints = XYClipper(nPoints, VBuffer);
if( nPoints == 0) return;
}
#ifdef FEATURE_VIDEOFX_IMPROVED
double polyZflt = 0.0;
for( i = 0; i < nVtx; ++i ) {
polyZflt += (double)vbuf[i].zv / (double)nVtx;
}
polyZ = polyZflt;
#else // FEATURE_VIDEOFX_IMPROVED
polyZ = 0;
for( i = 0; i < nVtx; ++i ) {
polyZ += vbuf[i].zv;
}
polyZ /= nVtx;
#endif // FEATURE_VIDEOFX_IMPROVED
InsertPoly_Gouraud(nPoints, (float)(polyZ - 0x20000), 0, 0, 0, POLY_HWR_trans);
}
void __cdecl InsertTransQuad_Sorted(int x, int y, int width, int height, int z) {
float x0, y0, x1, y1;
double rhw, sz;
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(z);
++Sort3dPtr;
*(Info3dPtr++) = POLY_HWR_trans;
*(Info3dPtr++) = 4; // vertex count
*(D3DTLVERTEX **)Info3dPtr = HWR_VertexPtr;
Info3dPtr += sizeof(D3DTLVERTEX *)/sizeof(__int16);
x0 = (float)x;
y0 = (float)y;
x1 = (float)(x + width);
y1 = (float)(y + height);
rhw = RhwFactor / (double)z;
sz = FltResZBuf - rhw * FltResZORhw;
HWR_VertexPtr[0].sx = x0;
HWR_VertexPtr[0].sy = y0;
HWR_VertexPtr[1].sx = x1;
HWR_VertexPtr[1].sy = y0;
HWR_VertexPtr[2].sx = x1;
HWR_VertexPtr[2].sy = y1;
HWR_VertexPtr[3].sx = x0;
HWR_VertexPtr[3].sy = y1;
for( int i=0; i<4; ++i ) {
HWR_VertexPtr[i].color = 0x80000000; // half transparent black
HWR_VertexPtr[i].sz = sz; // NOTE: there was bug because of uninitialized sz and rhw
HWR_VertexPtr[i].rhw = rhw;
}
HWR_VertexPtr += 4;
++SurfaceCount;
}
#ifdef FEATURE_VIDEOFX_IMPROVED
void __cdecl InsertSprite(int z, int x0, int y0, int x1, int y1, int spriteIdx, __int16 shade, DWORD flags) {
if( CHK_ANY(flags, SPR_TINT) ) return; // tinted sprites are not supported by software renderer yet
#else // FEATURE_VIDEOFX_IMPROVED
void __cdecl InsertSprite(int z, int x0, int y0, int x1, int y1, int spriteIdx, __int16 shade) {
#endif // FEATURE_VIDEOFX_IMPROVED
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = MAKE_ZSORT(z);
++Sort3dPtr;
*(Info3dPtr++) = POLY_sprite;
*(Info3dPtr++) = x0;
*(Info3dPtr++) = y0;
*(Info3dPtr++) = x1;
*(Info3dPtr++) = y1;
*(Info3dPtr++) = spriteIdx;
*(Info3dPtr++) = shade;
++SurfaceCount;
}
/*
* Inject function
*/
void Inject_3Dinsert() {
INJECT(0x00405840, visible_zclip);
INJECT(0x004058B0, ZedClipper);
INJECT(0x004059F0, XYGUVClipper);
INJECT(0x00405F10, InsertObjectGT4);
INJECT(0x00406970, InsertObjectGT3);
INJECT(0x004071F0, XYGClipper);
INJECT(0x00407620, InsertObjectG4);
INJECT(0x00407A00, InsertObjectG3);
INJECT(0x00407D20, XYClipper);
INJECT(0x00407FF0, InsertTrans8);
INJECT(0x004084A0, InsertTransQuad);
INJECT(0x00408580, InsertFlatRect);
INJECT(0x00408650, InsertLine);
INJECT(0x00408710, InsertGT3_ZBuffered);
INJECT(0x00408D60, DrawClippedPoly_Textured);
INJECT(0x00408EA0, InsertGT4_ZBuffered);
INJECT(0x004092E0, InsertObjectGT4_ZBuffered);
INJECT(0x00409380, InsertObjectGT3_ZBuffered);
INJECT(0x00409430, InsertObjectG4_ZBuffered);
INJECT(0x004097D0, DrawPoly_Gouraud);
INJECT(0x004098D0, InsertObjectG3_ZBuffered);
INJECT(0x00409BB0, InsertFlatRect_ZBuffered);
INJECT(0x00409D80, InsertLine_ZBuffered);
INJECT(0x00409EC0, InsertGT3_Sorted);
INJECT(0x0040A5D0, InsertClippedPoly_Textured);
INJECT(0x0040A780, InsertGT4_Sorted);
INJECT(0x0040AC60, InsertObjectGT4_Sorted);
INJECT(0x0040ACF0, InsertObjectGT3_Sorted);
INJECT(0x0040AD90, InsertObjectG4_Sorted);
INJECT(0x0040B1D0, InsertPoly_Gouraud);
INJECT(0x0040B350, InsertObjectG3_Sorted);
INJECT(0x0040B6A0, InsertSprite_Sorted);
INJECT(0x0040B9F0, InsertFlatRect_Sorted);
INJECT(0x0040BB70, InsertLine_Sorted);
INJECT(0x0040BCA0, InsertTrans8_Sorted);
INJECT(0x0040BE40, InsertTransQuad_Sorted);
INJECT(0x0040BF80, InsertSprite);
}
================================================
FILE: 3dsystem/3dinsert.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef _3DINSERT_H_INCLUDED
#define _3DINSERT_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#ifdef FEATURE_VIDEOFX_IMPROVED
bool InsertObjectEM(__int16 *ptrObj, int vtxCount, D3DCOLOR tint, PHD_UV *em_uv);
#endif // FEATURE_VIDEOFX_IMPROVED
// NOTE: this function is not presented in the original game
void InsertGourQuad(int x0, int y0, int x1, int y1, int z, D3DCOLOR color0, D3DCOLOR color1, D3DCOLOR color2, D3DCOLOR color3);
BOOL __cdecl visible_zclip(PHD_VBUF *vtx0, PHD_VBUF *vtx1, PHD_VBUF *vtx2); // 0x00405840
int __cdecl ZedClipper(int vtxCount, POINT_INFO *pts, VERTEX_INFO *vtx); // 0x004058B0
int __cdecl XYGUVClipper(int vtxCount, VERTEX_INFO *vtx); // 0x004059F0
__int16 *__cdecl InsertObjectGT4(__int16 *ptrObj, int number, SORTTYPE sortType); // 0x00405F10
__int16 *__cdecl InsertObjectGT3(__int16 *ptrObj, int number, SORTTYPE sortType); // 0x00406970
int __cdecl XYGClipper(int vtxCount, VERTEX_INFO *vtx); // 0x004071F0
__int16 *__cdecl InsertObjectG4(__int16 *ptrObj, int number, SORTTYPE sortType); // 0x00407620
__int16 *__cdecl InsertObjectG3(__int16 *ptrObj, int number, SORTTYPE sortType); // 0x00407A00
int __cdecl XYClipper(int vtxCount, VERTEX_INFO *vtx); // 0x00407D20
void __cdecl InsertTrans8(PHD_VBUF *vbuf, __int16 shade); // 0x00407FF0
void __cdecl InsertTransQuad(int x, int y, int width, int height, int z); // 0x004084A0
void __cdecl InsertFlatRect(int x0, int y0, int x1, int y1, int z, BYTE colorIdx); // 0x00408580
void __cdecl InsertLine(int x0, int y0, int x1, int y1, int z, BYTE colorIdx); // 0x00408650
void __cdecl InsertGT3_ZBuffered(PHD_VBUF *vtx0, PHD_VBUF *vtx1, PHD_VBUF *vtx2, PHD_TEXTURE *texture, PHD_UV *uv0, PHD_UV *uv1, PHD_UV *uv2); // 0x00408710
void __cdecl DrawClippedPoly_Textured(int vtxCount); // 0x00408D60
void __cdecl InsertGT4_ZBuffered(PHD_VBUF *vtx0, PHD_VBUF *vtx1, PHD_VBUF *vtx2, PHD_VBUF *vtx3, PHD_TEXTURE *texture); // 0x00408EA0
__int16 *__cdecl InsertObjectGT4_ZBuffered(__int16 *ptrObj, int number, SORTTYPE sortType); // 0x004092E0
__int16 *__cdecl InsertObjectGT3_ZBuffered(__int16 *ptrObj, int number, SORTTYPE sortType); // 0x00409380
__int16 *__cdecl InsertObjectG4_ZBuffered(__int16 *ptrObj, int number, SORTTYPE sortType); // 0x00409430
void __cdecl DrawPoly_Gouraud(int vtxCount, int red, int green, int blue); // 0x004097D0
__int16 *__cdecl InsertObjectG3_ZBuffered(__int16 *ptrObj, int number, SORTTYPE sortType); // 0x004098D0
void __cdecl InsertFlatRect_ZBuffered(int x0, int y0, int x1, int y1, int z, BYTE colorIdx); // 0x00409BB0
void __cdecl InsertLine_ZBuffered(int x0, int y0, int x1, int y1, int z, BYTE colorIdx); // 0x00409D80
void __cdecl InsertGT3_Sorted(PHD_VBUF *vtx0, PHD_VBUF *vtx1, PHD_VBUF *vtx2, PHD_TEXTURE *texture, PHD_UV *uv0, PHD_UV *uv1, PHD_UV *uv2, SORTTYPE sortType); // 0x00409EC0
void __cdecl InsertClippedPoly_Textured(int vtxCount, float z, __int16 polyType, __int16 texPage); // 0x0040A5D0
void __cdecl InsertGT4_Sorted(PHD_VBUF *vtx0, PHD_VBUF *vtx1, PHD_VBUF *vtx2, PHD_VBUF *vtx3, PHD_TEXTURE *texture, SORTTYPE sortType); // 0x0040A780
__int16 *__cdecl InsertObjectGT4_Sorted(__int16 *ptrObj, int number, SORTTYPE sortType); // 0x0040AC60
__int16 *__cdecl InsertObjectGT3_Sorted(__int16 *ptrObj, int number, SORTTYPE sortType); // 0x0040ACF0
__int16 *__cdecl InsertObjectG4_Sorted(__int16 *ptrObj, int number, SORTTYPE sortType); // 0x0040AD90
void __cdecl InsertPoly_Gouraud(int vtxCount, float z, int red, int green, int blue, __int16 polyType); // 0x0040B1D0
__int16 *__cdecl InsertObjectG3_Sorted(__int16 *ptrObj, int number, SORTTYPE sortType); // 0x0040B350
#ifdef FEATURE_VIDEOFX_IMPROVED
void __cdecl InsertSprite_Sorted(int z, int x0, int y0, int x1, int y1, int spriteIdx, __int16 shade, DWORD flags); // 0x0040B6A0
#else // FEATURE_VIDEOFX_IMPROVED
void __cdecl InsertSprite_Sorted(int z, int x0, int y0, int x1, int y1, int spriteIdx, __int16 shade); // 0x0040B6A0
#endif // FEATURE_VIDEOFX_IMPROVED
void __cdecl InsertFlatRect_Sorted(int x0, int y0, int x1, int y1, int z, BYTE colorIdx); // 0x0040B9F0
void __cdecl InsertLine_Sorted(int x0, int y0, int x1, int y1, int z, BYTE colorIdx); // 0x0040BB70
void __cdecl InsertTrans8_Sorted(PHD_VBUF *vbuf, __int16 shade); // 0x0040BCA0
void __cdecl InsertTransQuad_Sorted(int x, int y, int width, int height, int z); // 0x0040BE40
#ifdef FEATURE_VIDEOFX_IMPROVED
void __cdecl InsertSprite(int z, int x0, int y0, int x1, int y1, int spriteIdx, __int16 shade, DWORD flags); // 0x0040BF80
#else // FEATURE_VIDEOFX_IMPROVED
void __cdecl InsertSprite(int z, int x0, int y0, int x1, int y1, int spriteIdx, __int16 shade); // 0x0040BF80
#endif // FEATURE_VIDEOFX_IMPROVED
#endif // _3DINSERT_H_INCLUDED
================================================
FILE: 3dsystem/phd_math.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "3dsystem/phd_math.h"
#include "global/vars.h"
// Arctan base table
static int AtanBaseTable[8] = {
0x0000,
-0x4000,
-0xFFFF,
0xC000,
-0x8000,
0x4000,
0x8000,
-0xC000,
};
// Arctan angle table
static __int16 AtanAngleTable[0x802] = {
0x0000, 0x0005, 0x000A, 0x000F, 0x0014, 0x0019, 0x001F, 0x0024,
0x0029, 0x002E, 0x0033, 0x0038, 0x003D, 0x0042, 0x0047, 0x004C,
0x0051, 0x0057, 0x005C, 0x0061, 0x0066, 0x006B, 0x0070, 0x0075,
0x007A, 0x007F, 0x0084, 0x008A, 0x008F, 0x0094, 0x0099, 0x009E,
0x00A3, 0x00A8, 0x00AD, 0x00B2, 0x00B7, 0x00BC, 0x00C2, 0x00C7,
0x00CC, 0x00D1, 0x00D6, 0x00DB, 0x00E0, 0x00E5, 0x00EA, 0x00EF,
0x00F4, 0x00FA, 0x00FF, 0x0104, 0x0109, 0x010E, 0x0113, 0x0118,
0x011D, 0x0122, 0x0127, 0x012C, 0x0131, 0x0137, 0x013C, 0x0141,
0x0146, 0x014B, 0x0150, 0x0155, 0x015A, 0x015F, 0x0164, 0x0169,
0x016F, 0x0174, 0x0179, 0x017E, 0x0183, 0x0188, 0x018D, 0x0192,
0x0197, 0x019C, 0x01A1, 0x01A6, 0x01AC, 0x01B1, 0x01B6, 0x01BB,
0x01C0, 0x01C5, 0x01CA, 0x01CF, 0x01D4, 0x01D9, 0x01DE, 0x01E3,
0x01E9, 0x01EE, 0x01F3, 0x01F8, 0x01FD, 0x0202, 0x0207, 0x020C,
0x0211, 0x0216, 0x021B, 0x0220, 0x0226, 0x022B, 0x0230, 0x0235,
0x023A, 0x023F, 0x0244, 0x0249, 0x024E, 0x0253, 0x0258, 0x025D,
0x0262, 0x0268, 0x026D, 0x0272, 0x0277, 0x027C, 0x0281, 0x0286,
0x028B, 0x0290, 0x0295, 0x029A, 0x029F, 0x02A4, 0x02A9, 0x02AF,
0x02B4, 0x02B9, 0x02BE, 0x02C3, 0x02C8, 0x02CD, 0x02D2, 0x02D7,
0x02DC, 0x02E1, 0x02E6, 0x02EB, 0x02F0, 0x02F6, 0x02FB, 0x0300,
0x0305, 0x030A, 0x030F, 0x0314, 0x0319, 0x031E, 0x0323, 0x0328,
0x032D, 0x0332, 0x0337, 0x033C, 0x0341, 0x0347, 0x034C, 0x0351,
0x0356, 0x035B, 0x0360, 0x0365, 0x036A, 0x036F, 0x0374, 0x0379,
0x037E, 0x0383, 0x0388, 0x038D, 0x0392, 0x0397, 0x039C, 0x03A2,
0x03A7, 0x03AC, 0x03B1, 0x03B6, 0x03BB, 0x03C0, 0x03C5, 0x03CA,
0x03CF, 0x03D4, 0x03D9, 0x03DE, 0x03E3, 0x03E8, 0x03ED, 0x03F2,
0x03F7, 0x03FC, 0x0401, 0x0407, 0x040C, 0x0411, 0x0416, 0x041B,
0x0420, 0x0425, 0x042A, 0x042F, 0x0434, 0x0439, 0x043E, 0x0443,
0x0448, 0x044D, 0x0452, 0x0457, 0x045C, 0x0461, 0x0466, 0x046B,
0x0470, 0x0475, 0x047A, 0x047F, 0x0484, 0x0489, 0x048E, 0x0494,
0x0499, 0x049E, 0x04A3, 0x04A8, 0x04AD, 0x04B2, 0x04B7, 0x04BC,
0x04C1, 0x04C6, 0x04CB, 0x04D0, 0x04D5, 0x04DA, 0x04DF, 0x04E4,
0x04E9, 0x04EE, 0x04F3, 0x04F8, 0x04FD, 0x0502, 0x0507, 0x050C,
0x0511, 0x0516, 0x051B, 0x0520, 0x0525, 0x052A, 0x052F, 0x0534,
0x0539, 0x053E, 0x0543, 0x0548, 0x054D, 0x0552, 0x0557, 0x055C,
0x0561, 0x0566, 0x056B, 0x0570, 0x0575, 0x057A, 0x057F, 0x0584,
0x0589, 0x058E, 0x0593, 0x0598, 0x059D, 0x05A2, 0x05A7, 0x05AC,
0x05B1, 0x05B6, 0x05BB, 0x05C0, 0x05C5, 0x05CA, 0x05CF, 0x05D4,
0x05D9, 0x05DE, 0x05E3, 0x05E8, 0x05ED, 0x05F2, 0x05F7, 0x05FC,
0x0601, 0x0606, 0x060B, 0x0610, 0x0615, 0x061A, 0x061F, 0x0624,
0x0629, 0x062E, 0x0633, 0x0638, 0x063D, 0x0642, 0x0647, 0x064C,
0x0651, 0x0656, 0x065B, 0x0660, 0x0665, 0x066A, 0x066E, 0x0673,
0x0678, 0x067D, 0x0682, 0x0687, 0x068C, 0x0691, 0x0696, 0x069B,
0x06A0, 0x06A5, 0x06AA, 0x06AF, 0x06B4, 0x06B9, 0x06BE, 0x06C3,
0x06C8, 0x06CD, 0x06D2, 0x06D7, 0x06DC, 0x06E1, 0x06E5, 0x06EA,
0x06EF, 0x06F4, 0x06F9, 0x06FE, 0x0703, 0x0708, 0x070D, 0x0712,
0x0717, 0x071C, 0x0721, 0x0726, 0x072B, 0x0730, 0x0735, 0x0739,
0x073E, 0x0743, 0x0748, 0x074D, 0x0752, 0x0757, 0x075C, 0x0761,
0x0766, 0x076B, 0x0770, 0x0775, 0x077A, 0x077E, 0x0783, 0x0788,
0x078D, 0x0792, 0x0797, 0x079C, 0x07A1, 0x07A6, 0x07AB, 0x07B0,
0x07B5, 0x07B9, 0x07BE, 0x07C3, 0x07C8, 0x07CD, 0x07D2, 0x07D7,
0x07DC, 0x07E1, 0x07E6, 0x07EB, 0x07EF, 0x07F4, 0x07F9, 0x07FE,
0x0803, 0x0808, 0x080D, 0x0812, 0x0817, 0x081C, 0x0820, 0x0825,
0x082A, 0x082F, 0x0834, 0x0839, 0x083E, 0x0843, 0x0848, 0x084C,
0x0851, 0x0856, 0x085B, 0x0860, 0x0865, 0x086A, 0x086F, 0x0873,
0x0878, 0x087D, 0x0882, 0x0887, 0x088C, 0x0891, 0x0896, 0x089A,
0x089F, 0x08A4, 0x08A9, 0x08AE, 0x08B3, 0x08B8, 0x08BD, 0x08C1,
0x08C6, 0x08CB, 0x08D0, 0x08D5, 0x08DA, 0x08DF, 0x08E3, 0x08E8,
0x08ED, 0x08F2, 0x08F7, 0x08FC, 0x0901, 0x0905, 0x090A, 0x090F,
0x0914, 0x0919, 0x091E, 0x0922, 0x0927, 0x092C, 0x0931, 0x0936,
0x093B, 0x093F, 0x0944, 0x0949, 0x094E, 0x0953, 0x0958, 0x095C,
0x0961, 0x0966, 0x096B, 0x0970, 0x0975, 0x0979, 0x097E, 0x0983,
0x0988, 0x098D, 0x0992, 0x0996, 0x099B, 0x09A0, 0x09A5, 0x09AA,
0x09AE, 0x09B3, 0x09B8, 0x09BD, 0x09C2, 0x09C6, 0x09CB, 0x09D0,
0x09D5, 0x09DA, 0x09DE, 0x09E3, 0x09E8, 0x09ED, 0x09F2, 0x09F6,
0x09FB, 0x0A00, 0x0A05, 0x0A0A, 0x0A0E, 0x0A13, 0x0A18, 0x0A1D,
0x0A22, 0x0A26, 0x0A2B, 0x0A30, 0x0A35, 0x0A39, 0x0A3E, 0x0A43,
0x0A48, 0x0A4D, 0x0A51, 0x0A56, 0x0A5B, 0x0A60, 0x0A64, 0x0A69,
0x0A6E, 0x0A73, 0x0A77, 0x0A7C, 0x0A81, 0x0A86, 0x0A8B, 0x0A8F,
0x0A94, 0x0A99, 0x0A9E, 0x0AA2, 0x0AA7, 0x0AAC, 0x0AB1, 0x0AB5,
0x0ABA, 0x0ABF, 0x0AC4, 0x0AC8, 0x0ACD, 0x0AD2, 0x0AD7, 0x0ADB,
0x0AE0, 0x0AE5, 0x0AE9, 0x0AEE, 0x0AF3, 0x0AF8, 0x0AFC, 0x0B01,
0x0B06, 0x0B0B, 0x0B0F, 0x0B14, 0x0B19, 0x0B1E, 0x0B22, 0x0B27,
0x0B2C, 0x0B30, 0x0B35, 0x0B3A, 0x0B3F, 0x0B43, 0x0B48, 0x0B4D,
0x0B51, 0x0B56, 0x0B5B, 0x0B60, 0x0B64, 0x0B69, 0x0B6E, 0x0B72,
0x0B77, 0x0B7C, 0x0B80, 0x0B85, 0x0B8A, 0x0B8F, 0x0B93, 0x0B98,
0x0B9D, 0x0BA1, 0x0BA6, 0x0BAB, 0x0BAF, 0x0BB4, 0x0BB9, 0x0BBD,
0x0BC2, 0x0BC7, 0x0BCB, 0x0BD0, 0x0BD5, 0x0BD9, 0x0BDE, 0x0BE3,
0x0BE7, 0x0BEC, 0x0BF1, 0x0BF5, 0x0BFA, 0x0BFF, 0x0C03, 0x0C08,
0x0C0D, 0x0C11, 0x0C16, 0x0C1B, 0x0C1F, 0x0C24, 0x0C29, 0x0C2D,
0x0C32, 0x0C37, 0x0C3B, 0x0C40, 0x0C45, 0x0C49, 0x0C4E, 0x0C53,
0x0C57, 0x0C5C, 0x0C60, 0x0C65, 0x0C6A, 0x0C6E, 0x0C73, 0x0C78,
0x0C7C, 0x0C81, 0x0C86, 0x0C8A, 0x0C8F, 0x0C93, 0x0C98, 0x0C9D,
0x0CA1, 0x0CA6, 0x0CAB, 0x0CAF, 0x0CB4, 0x0CB8, 0x0CBD, 0x0CC2,
0x0CC6, 0x0CCB, 0x0CCF, 0x0CD4, 0x0CD9, 0x0CDD, 0x0CE2, 0x0CE6,
0x0CEB, 0x0CF0, 0x0CF4, 0x0CF9, 0x0CFD, 0x0D02, 0x0D07, 0x0D0B,
0x0D10, 0x0D14, 0x0D19, 0x0D1E, 0x0D22, 0x0D27, 0x0D2B, 0x0D30,
0x0D34, 0x0D39, 0x0D3E, 0x0D42, 0x0D47, 0x0D4B, 0x0D50, 0x0D54,
0x0D59, 0x0D5E, 0x0D62, 0x0D67, 0x0D6B, 0x0D70, 0x0D74, 0x0D79,
0x0D7D, 0x0D82, 0x0D87, 0x0D8B, 0x0D90, 0x0D94, 0x0D99, 0x0D9D,
0x0DA2, 0x0DA6, 0x0DAB, 0x0DAF, 0x0DB4, 0x0DB9, 0x0DBD, 0x0DC2,
0x0DC6, 0x0DCB, 0x0DCF, 0x0DD4, 0x0DD8, 0x0DDD, 0x0DE1, 0x0DE6,
0x0DEA, 0x0DEF, 0x0DF3, 0x0DF8, 0x0DFC, 0x0E01, 0x0E05, 0x0E0A,
0x0E0F, 0x0E13, 0x0E18, 0x0E1C, 0x0E21, 0x0E25, 0x0E2A, 0x0E2E,
0x0E33, 0x0E37, 0x0E3C, 0x0E40, 0x0E45, 0x0E49, 0x0E4E, 0x0E52,
0x0E56, 0x0E5B, 0x0E5F, 0x0E64, 0x0E68, 0x0E6D, 0x0E71, 0x0E76,
0x0E7A, 0x0E7F, 0x0E83, 0x0E88, 0x0E8C, 0x0E91, 0x0E95, 0x0E9A,
0x0E9E, 0x0EA3, 0x0EA7, 0x0EAC, 0x0EB0, 0x0EB4, 0x0EB9, 0x0EBD,
0x0EC2, 0x0EC6, 0x0ECB, 0x0ECF, 0x0ED4, 0x0ED8, 0x0EDC, 0x0EE1,
0x0EE5, 0x0EEA, 0x0EEE, 0x0EF3, 0x0EF7, 0x0EFC, 0x0F00, 0x0F04,
0x0F09, 0x0F0D, 0x0F12, 0x0F16, 0x0F1B, 0x0F1F, 0x0F23, 0x0F28,
0x0F2C, 0x0F31, 0x0F35, 0x0F3A, 0x0F3E, 0x0F42, 0x0F47, 0x0F4B,
0x0F50, 0x0F54, 0x0F58, 0x0F5D, 0x0F61, 0x0F66, 0x0F6A, 0x0F6E,
0x0F73, 0x0F77, 0x0F7C, 0x0F80, 0x0F84, 0x0F89, 0x0F8D, 0x0F91,
0x0F96, 0x0F9A, 0x0F9F, 0x0FA3, 0x0FA7, 0x0FAC, 0x0FB0, 0x0FB5,
0x0FB9, 0x0FBD, 0x0FC2, 0x0FC6, 0x0FCA, 0x0FCF, 0x0FD3, 0x0FD7,
0x0FDC, 0x0FE0, 0x0FE5, 0x0FE9, 0x0FED, 0x0FF2, 0x0FF6, 0x0FFA,
0x0FFF, 0x1003, 0x1007, 0x100C, 0x1010, 0x1014, 0x1019, 0x101D,
0x1021, 0x1026, 0x102A, 0x102E, 0x1033, 0x1037, 0x103B, 0x1040,
0x1044, 0x1048, 0x104D, 0x1051, 0x1055, 0x105A, 0x105E, 0x1062,
0x1067, 0x106B, 0x106F, 0x1073, 0x1078, 0x107C, 0x1080, 0x1085,
0x1089, 0x108D, 0x1092, 0x1096, 0x109A, 0x109E, 0x10A3, 0x10A7,
0x10AB, 0x10B0, 0x10B4, 0x10B8, 0x10BC, 0x10C1, 0x10C5, 0x10C9,
0x10CE, 0x10D2, 0x10D6, 0x10DA, 0x10DF, 0x10E3, 0x10E7, 0x10EB,
0x10F0, 0x10F4, 0x10F8, 0x10FD, 0x1101, 0x1105, 0x1109, 0x110E,
0x1112, 0x1116, 0x111A, 0x111F, 0x1123, 0x1127, 0x112B, 0x1130,
0x1134, 0x1138, 0x113C, 0x1140, 0x1145, 0x1149, 0x114D, 0x1151,
0x1156, 0x115A, 0x115E, 0x1162, 0x1166, 0x116B, 0x116F, 0x1173,
0x1177, 0x117C, 0x1180, 0x1184, 0x1188, 0x118C, 0x1191, 0x1195,
0x1199, 0x119D, 0x11A1, 0x11A6, 0x11AA, 0x11AE, 0x11B2, 0x11B6,
0x11BB, 0x11BF, 0x11C3, 0x11C7, 0x11CB, 0x11CF, 0x11D4, 0x11D8,
0x11DC, 0x11E0, 0x11E4, 0x11E9, 0x11ED, 0x11F1, 0x11F5, 0x11F9,
0x11FD, 0x1202, 0x1206, 0x120A, 0x120E, 0x1212, 0x1216, 0x121A,
0x121F, 0x1223, 0x1227, 0x122B, 0x122F, 0x1233, 0x1237, 0x123C,
0x1240, 0x1244, 0x1248, 0x124C, 0x1250, 0x1254, 0x1259, 0x125D,
0x1261, 0x1265, 0x1269, 0x126D, 0x1271, 0x1275, 0x127A, 0x127E,
0x1282, 0x1286, 0x128A, 0x128E, 0x1292, 0x1296, 0x129A, 0x129F,
0x12A3, 0x12A7, 0x12AB, 0x12AF, 0x12B3, 0x12B7, 0x12BB, 0x12BF,
0x12C3, 0x12C7, 0x12CC, 0x12D0, 0x12D4, 0x12D8, 0x12DC, 0x12E0,
0x12E4, 0x12E8, 0x12EC, 0x12F0, 0x12F4, 0x12F8, 0x12FC, 0x1301,
0x1305, 0x1309, 0x130D, 0x1311, 0x1315, 0x1319, 0x131D, 0x1321,
0x1325, 0x1329, 0x132D, 0x1331, 0x1335, 0x1339, 0x133D, 0x1341,
0x1345, 0x1349, 0x134D, 0x1351, 0x1355, 0x135A, 0x135E, 0x1362,
0x1366, 0x136A, 0x136E, 0x1372, 0x1376, 0x137A, 0x137E, 0x1382,
0x1386, 0x138A, 0x138E, 0x1392, 0x1396, 0x139A, 0x139E, 0x13A2,
0x13A6, 0x13AA, 0x13AE, 0x13B2, 0x13B6, 0x13BA, 0x13BE, 0x13C2,
0x13C6, 0x13CA, 0x13CE, 0x13D2, 0x13D6, 0x13DA, 0x13DE, 0x13E2,
0x13E6, 0x13E9, 0x13ED, 0x13F1, 0x13F5, 0x13F9, 0x13FD, 0x1401,
0x1405, 0x1409, 0x140D, 0x1411, 0x1415, 0x1419, 0x141D, 0x1421,
0x1425, 0x1429, 0x142D, 0x1431, 0x1435, 0x1439, 0x143D, 0x1440,
0x1444, 0x1448, 0x144C, 0x1450, 0x1454, 0x1458, 0x145C, 0x1460,
0x1464, 0x1468, 0x146C, 0x1470, 0x1473, 0x1477, 0x147B, 0x147F,
0x1483, 0x1487, 0x148B, 0x148F, 0x1493, 0x1497, 0x149B, 0x149E,
0x14A2, 0x14A6, 0x14AA, 0x14AE, 0x14B2, 0x14B6, 0x14BA, 0x14BE,
0x14C1, 0x14C5, 0x14C9, 0x14CD, 0x14D1, 0x14D5, 0x14D9, 0x14DD,
0x14E0, 0x14E4, 0x14E8, 0x14EC, 0x14F0, 0x14F4, 0x14F8, 0x14FB,
0x14FF, 0x1503, 0x1507, 0x150B, 0x150F, 0x1513, 0x1516, 0x151A,
0x151E, 0x1522, 0x1526, 0x152A, 0x152D, 0x1531, 0x1535, 0x1539,
0x153D, 0x1541, 0x1544, 0x1548, 0x154C, 0x1550, 0x1554, 0x1558,
0x155B, 0x155F, 0x1563, 0x1567, 0x156B, 0x156E, 0x1572, 0x1576,
0x157A, 0x157E, 0x1581, 0x1585, 0x1589, 0x158D, 0x1591, 0x1594,
0x1598, 0x159C, 0x15A0, 0x15A4, 0x15A7, 0x15AB, 0x15AF, 0x15B3,
0x15B7, 0x15BA, 0x15BE, 0x15C2, 0x15C6, 0x15C9, 0x15CD, 0x15D1,
0x15D5, 0x15D8, 0x15DC, 0x15E0, 0x15E4, 0x15E8, 0x15EB, 0x15EF,
0x15F3, 0x15F7, 0x15FA, 0x15FE, 0x1602, 0x1606, 0x1609, 0x160D,
0x1611, 0x1614, 0x1618, 0x161C, 0x1620, 0x1623, 0x1627, 0x162B,
0x162F, 0x1632, 0x1636, 0x163A, 0x163E, 0x1641, 0x1645, 0x1649,
0x164C, 0x1650, 0x1654, 0x1658, 0x165B, 0x165F, 0x1663, 0x1666,
0x166A, 0x166E, 0x1671, 0x1675, 0x1679, 0x167D, 0x1680, 0x1684,
0x1688, 0x168B, 0x168F, 0x1693, 0x1696, 0x169A, 0x169E, 0x16A1,
0x16A5, 0x16A9, 0x16AC, 0x16B0, 0x16B4, 0x16B7, 0x16BB, 0x16BF,
0x16C2, 0x16C6, 0x16CA, 0x16CD, 0x16D1, 0x16D5, 0x16D8, 0x16DC,
0x16E0, 0x16E3, 0x16E7, 0x16EB, 0x16EE, 0x16F2, 0x16F6, 0x16F9,
0x16FD, 0x1700, 0x1704, 0x1708, 0x170B, 0x170F, 0x1713, 0x1716,
0x171A, 0x171D, 0x1721, 0x1725, 0x1728, 0x172C, 0x1730, 0x1733,
0x1737, 0x173A, 0x173E, 0x1742, 0x1745, 0x1749, 0x174C, 0x1750,
0x1754, 0x1757, 0x175B, 0x175E, 0x1762, 0x1766, 0x1769, 0x176D,
0x1770, 0x1774, 0x1778, 0x177B, 0x177F, 0x1782, 0x1786, 0x1789,
0x178D, 0x1791, 0x1794, 0x1798, 0x179B, 0x179F, 0x17A2, 0x17A6,
0x17AA, 0x17AD, 0x17B1, 0x17B4, 0x17B8, 0x17BB, 0x17BF, 0x17C2,
0x17C6, 0x17C9, 0x17CD, 0x17D1, 0x17D4, 0x17D8, 0x17DB, 0x17DF,
0x17E2, 0x17E6, 0x17E9, 0x17ED, 0x17F0, 0x17F4, 0x17F7, 0x17FB,
0x17FE, 0x1802, 0x1806, 0x1809, 0x180D, 0x1810, 0x1814, 0x1817,
0x181B, 0x181E, 0x1822, 0x1825, 0x1829, 0x182C, 0x1830, 0x1833,
0x1837, 0x183A, 0x183E, 0x1841, 0x1845, 0x1848, 0x184C, 0x184F,
0x1853, 0x1856, 0x185A, 0x185D, 0x1860, 0x1864, 0x1867, 0x186B,
0x186E, 0x1872, 0x1875, 0x1879, 0x187C, 0x1880, 0x1883, 0x1887,
0x188A, 0x188E, 0x1891, 0x1894, 0x1898, 0x189B, 0x189F, 0x18A2,
0x18A6, 0x18A9, 0x18AD, 0x18B0, 0x18B3, 0x18B7, 0x18BA, 0x18BE,
0x18C1, 0x18C5, 0x18C8, 0x18CC, 0x18CF, 0x18D2, 0x18D6, 0x18D9,
0x18DD, 0x18E0, 0x18E3, 0x18E7, 0x18EA, 0x18EE, 0x18F1, 0x18F5,
0x18F8, 0x18FB, 0x18FF, 0x1902, 0x1906, 0x1909, 0x190C, 0x1910,
0x1913, 0x1917, 0x191A, 0x191D, 0x1921, 0x1924, 0x1928, 0x192B,
0x192E, 0x1932, 0x1935, 0x1938, 0x193C, 0x193F, 0x1943, 0x1946,
0x1949, 0x194D, 0x1950, 0x1953, 0x1957, 0x195A, 0x195D, 0x1961,
0x1964, 0x1968, 0x196B, 0x196E, 0x1972, 0x1975, 0x1978, 0x197C,
0x197F, 0x1982, 0x1986, 0x1989, 0x198C, 0x1990, 0x1993, 0x1996,
0x199A, 0x199D, 0x19A0, 0x19A4, 0x19A7, 0x19AA, 0x19AE, 0x19B1,
0x19B4, 0x19B8, 0x19BB, 0x19BE, 0x19C2, 0x19C5, 0x19C8, 0x19CC,
0x19CF, 0x19D2, 0x19D5, 0x19D9, 0x19DC, 0x19DF, 0x19E3, 0x19E6,
0x19E9, 0x19ED, 0x19F0, 0x19F3, 0x19F6, 0x19FA, 0x19FD, 0x1A00,
0x1A04, 0x1A07, 0x1A0A, 0x1A0D, 0x1A11, 0x1A14, 0x1A17, 0x1A1B,
0x1A1E, 0x1A21, 0x1A24, 0x1A28, 0x1A2B, 0x1A2E, 0x1A31, 0x1A35,
0x1A38, 0x1A3B, 0x1A3E, 0x1A42, 0x1A45, 0x1A48, 0x1A4B, 0x1A4F,
0x1A52, 0x1A55, 0x1A58, 0x1A5C, 0x1A5F, 0x1A62, 0x1A65, 0x1A69,
0x1A6C, 0x1A6F, 0x1A72, 0x1A76, 0x1A79, 0x1A7C, 0x1A7F, 0x1A83,
0x1A86, 0x1A89, 0x1A8C, 0x1A8F, 0x1A93, 0x1A96, 0x1A99, 0x1A9C,
0x1A9F, 0x1AA3, 0x1AA6, 0x1AA9, 0x1AAC, 0x1AB0, 0x1AB3, 0x1AB6,
0x1AB9, 0x1ABC, 0x1AC0, 0x1AC3, 0x1AC6, 0x1AC9, 0x1ACC, 0x1ACF,
0x1AD3, 0x1AD6, 0x1AD9, 0x1ADC, 0x1ADF, 0x1AE3, 0x1AE6, 0x1AE9,
0x1AEC, 0x1AEF, 0x1AF2, 0x1AF6, 0x1AF9, 0x1AFC, 0x1AFF, 0x1B02,
0x1B05, 0x1B09, 0x1B0C, 0x1B0F, 0x1B12, 0x1B15, 0x1B18, 0x1B1C,
0x1B1F, 0x1B22, 0x1B25, 0x1B28, 0x1B2B, 0x1B2E, 0x1B32, 0x1B35,
0x1B38, 0x1B3B, 0x1B3E, 0x1B41, 0x1B44, 0x1B48, 0x1B4B, 0x1B4E,
0x1B51, 0x1B54, 0x1B57, 0x1B5A, 0x1B5D, 0x1B61, 0x1B64, 0x1B67,
0x1B6A, 0x1B6D, 0x1B70, 0x1B73, 0x1B76, 0x1B79, 0x1B7D, 0x1B80,
0x1B83, 0x1B86, 0x1B89, 0x1B8C, 0x1B8F, 0x1B92, 0x1B95, 0x1B98,
0x1B9C, 0x1B9F, 0x1BA2, 0x1BA5, 0x1BA8, 0x1BAB, 0x1BAE, 0x1BB1,
0x1BB4, 0x1BB7, 0x1BBA, 0x1BBD, 0x1BC1, 0x1BC4, 0x1BC7, 0x1BCA,
0x1BCD, 0x1BD0, 0x1BD3, 0x1BD6, 0x1BD9, 0x1BDC, 0x1BDF, 0x1BE2,
0x1BE5, 0x1BE8, 0x1BEB, 0x1BEE, 0x1BF2, 0x1BF5, 0x1BF8, 0x1BFB,
0x1BFE, 0x1C01, 0x1C04, 0x1C07, 0x1C0A, 0x1C0D, 0x1C10, 0x1C13,
0x1C16, 0x1C19, 0x1C1C, 0x1C1F, 0x1C22, 0x1C25, 0x1C28, 0x1C2B,
0x1C2E, 0x1C31, 0x1C34, 0x1C37, 0x1C3A, 0x1C3D, 0x1C40, 0x1C43,
0x1C46, 0x1C49, 0x1C4C, 0x1C4F, 0x1C52, 0x1C55, 0x1C58, 0x1C5B,
0x1C5E, 0x1C61, 0x1C64, 0x1C67, 0x1C6A, 0x1C6D, 0x1C70, 0x1C73,
0x1C76, 0x1C79, 0x1C7C, 0x1C7F, 0x1C82, 0x1C85, 0x1C88, 0x1C8B,
0x1C8E, 0x1C91, 0x1C94, 0x1C97, 0x1C9A, 0x1C9D, 0x1CA0, 0x1CA3,
0x1CA6, 0x1CA9, 0x1CAC, 0x1CAF, 0x1CB2, 0x1CB5, 0x1CB8, 0x1CBB,
0x1CBE, 0x1CC1, 0x1CC3, 0x1CC6, 0x1CC9, 0x1CCC, 0x1CCF, 0x1CD2,
0x1CD5, 0x1CD8, 0x1CDB, 0x1CDE, 0x1CE1, 0x1CE4, 0x1CE7, 0x1CEA,
0x1CED, 0x1CF0, 0x1CF3, 0x1CF5, 0x1CF8, 0x1CFB, 0x1CFE, 0x1D01,
0x1D04, 0x1D07, 0x1D0A, 0x1D0D, 0x1D10, 0x1D13, 0x1D16, 0x1D18,
0x1D1B, 0x1D1E, 0x1D21, 0x1D24, 0x1D27, 0x1D2A, 0x1D2D, 0x1D30,
0x1D33, 0x1D35, 0x1D38, 0x1D3B, 0x1D3E, 0x1D41, 0x1D44, 0x1D47,
0x1D4A, 0x1D4D, 0x1D4F, 0x1D52, 0x1D55, 0x1D58, 0x1D5B, 0x1D5E,
0x1D61, 0x1D64, 0x1D66, 0x1D69, 0x1D6C, 0x1D6F, 0x1D72, 0x1D75,
0x1D78, 0x1D7B, 0x1D7D, 0x1D80, 0x1D83, 0x1D86, 0x1D89, 0x1D8C,
0x1D8E, 0x1D91, 0x1D94, 0x1D97, 0x1D9A, 0x1D9D, 0x1DA0, 0x1DA2,
0x1DA5, 0x1DA8, 0x1DAB, 0x1DAE, 0x1DB1, 0x1DB3, 0x1DB6, 0x1DB9,
0x1DBC, 0x1DBF, 0x1DC2, 0x1DC4, 0x1DC7, 0x1DCA, 0x1DCD, 0x1DD0,
0x1DD3, 0x1DD5, 0x1DD8, 0x1DDB, 0x1DDE, 0x1DE1, 0x1DE3, 0x1DE6,
0x1DE9, 0x1DEC, 0x1DEF, 0x1DF1, 0x1DF4, 0x1DF7, 0x1DFA, 0x1DFD,
0x1DFF, 0x1E02, 0x1E05, 0x1E08, 0x1E0B, 0x1E0D, 0x1E10, 0x1E13,
0x1E16, 0x1E19, 0x1E1B, 0x1E1E, 0x1E21, 0x1E24, 0x1E26, 0x1E29,
0x1E2C, 0x1E2F, 0x1E32, 0x1E34, 0x1E37, 0x1E3A, 0x1E3D, 0x1E3F,
0x1E42, 0x1E45, 0x1E48, 0x1E4A, 0x1E4D, 0x1E50, 0x1E53, 0x1E55,
0x1E58, 0x1E5B, 0x1E5E, 0x1E60, 0x1E63, 0x1E66, 0x1E69, 0x1E6B,
0x1E6E, 0x1E71, 0x1E74, 0x1E76, 0x1E79, 0x1E7C, 0x1E7F, 0x1E81,
0x1E84, 0x1E87, 0x1E8A, 0x1E8C, 0x1E8F, 0x1E92, 0x1E94, 0x1E97,
0x1E9A, 0x1E9D, 0x1E9F, 0x1EA2, 0x1EA5, 0x1EA8, 0x1EAA, 0x1EAD,
0x1EB0, 0x1EB2, 0x1EB5, 0x1EB8, 0x1EBA, 0x1EBD, 0x1EC0, 0x1EC3,
0x1EC5, 0x1EC8, 0x1ECB, 0x1ECD, 0x1ED0, 0x1ED3, 0x1ED5, 0x1ED8,
0x1EDB, 0x1EDE, 0x1EE0, 0x1EE3, 0x1EE6, 0x1EE8, 0x1EEB, 0x1EEE,
0x1EF0, 0x1EF3, 0x1EF6, 0x1EF8, 0x1EFB, 0x1EFE, 0x1F00, 0x1F03,
0x1F06, 0x1F08, 0x1F0B, 0x1F0E, 0x1F10, 0x1F13, 0x1F16, 0x1F18,
0x1F1B, 0x1F1E, 0x1F20, 0x1F23, 0x1F26, 0x1F28, 0x1F2B, 0x1F2E,
0x1F30, 0x1F33, 0x1F36, 0x1F38, 0x1F3B, 0x1F3D, 0x1F40, 0x1F43,
0x1F45, 0x1F48, 0x1F4B, 0x1F4D, 0x1F50, 0x1F53, 0x1F55, 0x1F58,
0x1F5A, 0x1F5D, 0x1F60, 0x1F62, 0x1F65, 0x1F68, 0x1F6A, 0x1F6D,
0x1F6F, 0x1F72, 0x1F75, 0x1F77, 0x1F7A, 0x1F7C, 0x1F7F, 0x1F82,
0x1F84, 0x1F87, 0x1F8A, 0x1F8C, 0x1F8F, 0x1F91, 0x1F94, 0x1F97,
0x1F99, 0x1F9C, 0x1F9E, 0x1FA1, 0x1FA4, 0x1FA6, 0x1FA9, 0x1FAB,
0x1FAE, 0x1FB0, 0x1FB3, 0x1FB6, 0x1FB8, 0x1FBB, 0x1FBD, 0x1FC0,
0x1FC3, 0x1FC5, 0x1FC8, 0x1FCA, 0x1FCD, 0x1FCF, 0x1FD2, 0x1FD5,
0x1FD7, 0x1FDA, 0x1FDC, 0x1FDF, 0x1FE1, 0x1FE4, 0x1FE6, 0x1FE9,
0x1FEC, 0x1FEE, 0x1FF1, 0x1FF3, 0x1FF6, 0x1FF8, 0x1FFB, 0x1FFD,
0x2000, 0x2000,
};
// Sines integer representation table for angle 0..90 degrees
static const __int16 PhdSinTable[0x402] = {
0x0000, 0x0019, 0x0032, 0x004B, 0x0065, 0x007E, 0x0097, 0x00B0,
0x00C9, 0x00E2, 0x00FB, 0x0114, 0x012E, 0x0147, 0x0160, 0x0179,
0x0192, 0x01AB, 0x01C4, 0x01DD, 0x01F7, 0x0210, 0x0229, 0x0242,
0x025B, 0x0274, 0x028D, 0x02A6, 0x02C0, 0x02D9, 0x02F2, 0x030B,
0x0324, 0x033D, 0x0356, 0x036F, 0x0388, 0x03A1, 0x03BB, 0x03D4,
0x03ED, 0x0406, 0x041F, 0x0438, 0x0451, 0x046A, 0x0483, 0x049C,
0x04B5, 0x04CE, 0x04E7, 0x0500, 0x051A, 0x0533, 0x054C, 0x0565,
0x057E, 0x0597, 0x05B0, 0x05C9, 0x05E2, 0x05FB, 0x0614, 0x062D,
0x0646, 0x065F, 0x0678, 0x0691, 0x06AA, 0x06C3, 0x06DC, 0x06F5,
0x070E, 0x0727, 0x0740, 0x0759, 0x0772, 0x078B, 0x07A4, 0x07BD,
0x07D6, 0x07EF, 0x0807, 0x0820, 0x0839, 0x0852, 0x086B, 0x0884,
0x089D, 0x08B6, 0x08CF, 0x08E8, 0x0901, 0x0919, 0x0932, 0x094B,
0x0964, 0x097D, 0x0996, 0x09AF, 0x09C7, 0x09E0, 0x09F9, 0x0A12,
0x0A2B, 0x0A44, 0x0A5C, 0x0A75, 0x0A8E, 0x0AA7, 0x0AC0, 0x0AD8,
0x0AF1, 0x0B0A, 0x0B23, 0x0B3B, 0x0B54, 0x0B6D, 0x0B85, 0x0B9E,
0x0BB7, 0x0BD0, 0x0BE8, 0x0C01, 0x0C1A, 0x0C32, 0x0C4B, 0x0C64,
0x0C7C, 0x0C95, 0x0CAE, 0x0CC6, 0x0CDF, 0x0CF8, 0x0D10, 0x0D29,
0x0D41, 0x0D5A, 0x0D72, 0x0D8B, 0x0DA4, 0x0DBC, 0x0DD5, 0x0DED,
0x0E06, 0x0E1E, 0x0E37, 0x0E4F, 0x0E68, 0x0E80, 0x0E99, 0x0EB1,
0x0ECA, 0x0EE2, 0x0EFB, 0x0F13, 0x0F2B, 0x0F44, 0x0F5C, 0x0F75,
0x0F8D, 0x0FA5, 0x0FBE, 0x0FD6, 0x0FEE, 0x1007, 0x101F, 0x1037,
0x1050, 0x1068, 0x1080, 0x1099, 0x10B1, 0x10C9, 0x10E1, 0x10FA,
0x1112, 0x112A, 0x1142, 0x115A, 0x1173, 0x118B, 0x11A3, 0x11BB,
0x11D3, 0x11EB, 0x1204, 0x121C, 0x1234, 0x124C, 0x1264, 0x127C,
0x1294, 0x12AC, 0x12C4, 0x12DC, 0x12F4, 0x130C, 0x1324, 0x133C,
0x1354, 0x136C, 0x1384, 0x139C, 0x13B4, 0x13CC, 0x13E4, 0x13FB,
0x1413, 0x142B, 0x1443, 0x145B, 0x1473, 0x148B, 0x14A2, 0x14BA,
0x14D2, 0x14EA, 0x1501, 0x1519, 0x1531, 0x1549, 0x1560, 0x1578,
0x1590, 0x15A7, 0x15BF, 0x15D7, 0x15EE, 0x1606, 0x161D, 0x1635,
0x164C, 0x1664, 0x167C, 0x1693, 0x16AB, 0x16C2, 0x16DA, 0x16F1,
0x1709, 0x1720, 0x1737, 0x174F, 0x1766, 0x177E, 0x1795, 0x17AC,
0x17C4, 0x17DB, 0x17F2, 0x180A, 0x1821, 0x1838, 0x184F, 0x1867,
0x187E, 0x1895, 0x18AC, 0x18C3, 0x18DB, 0x18F2, 0x1909, 0x1920,
0x1937, 0x194E, 0x1965, 0x197C, 0x1993, 0x19AA, 0x19C1, 0x19D8,
0x19EF, 0x1A06, 0x1A1D, 0x1A34, 0x1A4B, 0x1A62, 0x1A79, 0x1A90,
0x1AA7, 0x1ABE, 0x1AD4, 0x1AEB, 0x1B02, 0x1B19, 0x1B30, 0x1B46,
0x1B5D, 0x1B74, 0x1B8A, 0x1BA1, 0x1BB8, 0x1BCE, 0x1BE5, 0x1BFC,
0x1C12, 0x1C29, 0x1C3F, 0x1C56, 0x1C6C, 0x1C83, 0x1C99, 0x1CB0,
0x1CC6, 0x1CDD, 0x1CF3, 0x1D0A, 0x1D20, 0x1D36, 0x1D4D, 0x1D63,
0x1D79, 0x1D90, 0x1DA6, 0x1DBC, 0x1DD3, 0x1DE9, 0x1DFF, 0x1E15,
0x1E2B, 0x1E42, 0x1E58, 0x1E6E, 0x1E84, 0x1E9A, 0x1EB0, 0x1EC6,
0x1EDC, 0x1EF2, 0x1F08, 0x1F1E, 0x1F34, 0x1F4A, 0x1F60, 0x1F76,
0x1F8C, 0x1FA2, 0x1FB7, 0x1FCD, 0x1FE3, 0x1FF9, 0x200F, 0x2024,
0x203A, 0x2050, 0x2065, 0x207B, 0x2091, 0x20A6, 0x20BC, 0x20D1,
0x20E7, 0x20FD, 0x2112, 0x2128, 0x213D, 0x2153, 0x2168, 0x217D,
0x2193, 0x21A8, 0x21BE, 0x21D3, 0x21E8, 0x21FE, 0x2213, 0x2228,
0x223D, 0x2253, 0x2268, 0x227D, 0x2292, 0x22A7, 0x22BC, 0x22D2,
0x22E7, 0x22FC, 0x2311, 0x2326, 0x233B, 0x2350, 0x2365, 0x237A,
0x238E, 0x23A3, 0x23B8, 0x23CD, 0x23E2, 0x23F7, 0x240B, 0x2420,
0x2435, 0x244A, 0x245E, 0x2473, 0x2488, 0x249C, 0x24B1, 0x24C5,
0x24DA, 0x24EF, 0x2503, 0x2518, 0x252C, 0x2541, 0x2555, 0x2569,
0x257E, 0x2592, 0x25A6, 0x25BB, 0x25CF, 0x25E3, 0x25F8, 0x260C,
0x2620, 0x2634, 0x2648, 0x265C, 0x2671, 0x2685, 0x2699, 0x26AD,
0x26C1, 0x26D5, 0x26E9, 0x26FD, 0x2711, 0x2724, 0x2738, 0x274C,
0x2760, 0x2774, 0x2788, 0x279B, 0x27AF, 0x27C3, 0x27D6, 0x27EA,
0x27FE, 0x2811, 0x2825, 0x2838, 0x284C, 0x2860, 0x2873, 0x2886,
0x289A, 0x28AD, 0x28C1, 0x28D4, 0x28E7, 0x28FB, 0x290E, 0x2921,
0x2935, 0x2948, 0x295B, 0x296E, 0x2981, 0x2994, 0x29A7, 0x29BB,
0x29CE, 0x29E1, 0x29F4, 0x2A07, 0x2A1A, 0x2A2C, 0x2A3F, 0x2A52,
0x2A65, 0x2A78, 0x2A8B, 0x2A9D, 0x2AB0, 0x2AC3, 0x2AD6, 0x2AE8,
0x2AFB, 0x2B0D, 0x2B20, 0x2B33, 0x2B45, 0x2B58, 0x2B6A, 0x2B7D,
0x2B8F, 0x2BA1, 0x2BB4, 0x2BC6, 0x2BD8, 0x2BEB, 0x2BFD, 0x2C0F,
0x2C21, 0x2C34, 0x2C46, 0x2C58, 0x2C6A, 0x2C7C, 0x2C8E, 0x2CA0,
0x2CB2, 0x2CC4, 0x2CD6, 0x2CE8, 0x2CFA, 0x2D0C, 0x2D1E, 0x2D2F,
0x2D41, 0x2D53, 0x2D65, 0x2D76, 0x2D88, 0x2D9A, 0x2DAB, 0x2DBD,
0x2DCF, 0x2DE0, 0x2DF2, 0x2E03, 0x2E15, 0x2E26, 0x2E37, 0x2E49,
0x2E5A, 0x2E6B, 0x2E7D, 0x2E8E, 0x2E9F, 0x2EB0, 0x2EC2, 0x2ED3,
0x2EE4, 0x2EF5, 0x2F06, 0x2F17, 0x2F28, 0x2F39, 0x2F4A, 0x2F5B,
0x2F6C, 0x2F7D, 0x2F8D, 0x2F9E, 0x2FAF, 0x2FC0, 0x2FD0, 0x2FE1,
0x2FF2, 0x3002, 0x3013, 0x3024, 0x3034, 0x3045, 0x3055, 0x3066,
0x3076, 0x3087, 0x3097, 0x30A7, 0x30B8, 0x30C8, 0x30D8, 0x30E8,
0x30F9, 0x3109, 0x3119, 0x3129, 0x3139, 0x3149, 0x3159, 0x3169,
0x3179, 0x3189, 0x3199, 0x31A9, 0x31B9, 0x31C8, 0x31D8, 0x31E8,
0x31F8, 0x3207, 0x3217, 0x3227, 0x3236, 0x3246, 0x3255, 0x3265,
0x3274, 0x3284, 0x3293, 0x32A3, 0x32B2, 0x32C1, 0x32D0, 0x32E0,
0x32EF, 0x32FE, 0x330D, 0x331D, 0x332C, 0x333B, 0x334A, 0x3359,
0x3368, 0x3377, 0x3386, 0x3395, 0x33A3, 0x33B2, 0x33C1, 0x33D0,
0x33DF, 0x33ED, 0x33FC, 0x340B, 0x3419, 0x3428, 0x3436, 0x3445,
0x3453, 0x3462, 0x3470, 0x347F, 0x348D, 0x349B, 0x34AA, 0x34B8,
0x34C6, 0x34D4, 0x34E2, 0x34F1, 0x34FF, 0x350D, 0x351B, 0x3529,
0x3537, 0x3545, 0x3553, 0x3561, 0x356E, 0x357C, 0x358A, 0x3598,
0x35A5, 0x35B3, 0x35C1, 0x35CE, 0x35DC, 0x35EA, 0x35F7, 0x3605,
0x3612, 0x3620, 0x362D, 0x363A, 0x3648, 0x3655, 0x3662, 0x366F,
0x367D, 0x368A, 0x3697, 0x36A4, 0x36B1, 0x36BE, 0x36CB, 0x36D8,
0x36E5, 0x36F2, 0x36FF, 0x370C, 0x3718, 0x3725, 0x3732, 0x373F,
0x374B, 0x3758, 0x3765, 0x3771, 0x377E, 0x378A, 0x3797, 0x37A3,
0x37B0, 0x37BC, 0x37C8, 0x37D5, 0x37E1, 0x37ED, 0x37F9, 0x3805,
0x3812, 0x381E, 0x382A, 0x3836, 0x3842, 0x384E, 0x385A, 0x3866,
0x3871, 0x387D, 0x3889, 0x3895, 0x38A1, 0x38AC, 0x38B8, 0x38C3,
0x38CF, 0x38DB, 0x38E6, 0x38F2, 0x38FD, 0x3909, 0x3914, 0x391F,
0x392B, 0x3936, 0x3941, 0x394C, 0x3958, 0x3963, 0x396E, 0x3979,
0x3984, 0x398F, 0x399A, 0x39A5, 0x39B0, 0x39BB, 0x39C5, 0x39D0,
0x39DB, 0x39E6, 0x39F0, 0x39FB, 0x3A06, 0x3A10, 0x3A1B, 0x3A25,
0x3A30, 0x3A3A, 0x3A45, 0x3A4F, 0x3A59, 0x3A64, 0x3A6E, 0x3A78,
0x3A82, 0x3A8D, 0x3A97, 0x3AA1, 0x3AAB, 0x3AB5, 0x3ABF, 0x3AC9,
0x3AD3, 0x3ADD, 0x3AE6, 0x3AF0, 0x3AFA, 0x3B04, 0x3B0E, 0x3B17,
0x3B21, 0x3B2A, 0x3B34, 0x3B3E, 0x3B47, 0x3B50, 0x3B5A, 0x3B63,
0x3B6D, 0x3B76, 0x3B7F, 0x3B88, 0x3B92, 0x3B9B, 0x3BA4, 0x3BAD,
0x3BB6, 0x3BBF, 0x3BC8, 0x3BD1, 0x3BDA, 0x3BE3, 0x3BEC, 0x3BF5,
0x3BFD, 0x3C06, 0x3C0F, 0x3C17, 0x3C20, 0x3C29, 0x3C31, 0x3C3A,
0x3C42, 0x3C4B, 0x3C53, 0x3C5B, 0x3C64, 0x3C6C, 0x3C74, 0x3C7D,
0x3C85, 0x3C8D, 0x3C95, 0x3C9D, 0x3CA5, 0x3CAD, 0x3CB5, 0x3CBD,
0x3CC5, 0x3CCD, 0x3CD5, 0x3CDD, 0x3CE4, 0x3CEC, 0x3CF4, 0x3CFB,
0x3D03, 0x3D0B, 0x3D12, 0x3D1A, 0x3D21, 0x3D28, 0x3D30, 0x3D37,
0x3D3F, 0x3D46, 0x3D4D, 0x3D54, 0x3D5B, 0x3D63, 0x3D6A, 0x3D71,
0x3D78, 0x3D7F, 0x3D86, 0x3D8D, 0x3D93, 0x3D9A, 0x3DA1, 0x3DA8,
0x3DAF, 0x3DB5, 0x3DBC, 0x3DC2, 0x3DC9, 0x3DD0, 0x3DD6, 0x3DDD,
0x3DE3, 0x3DE9, 0x3DF0, 0x3DF6, 0x3DFC, 0x3E03, 0x3E09, 0x3E0F,
0x3E15, 0x3E1B, 0x3E21, 0x3E27, 0x3E2D, 0x3E33, 0x3E39, 0x3E3F,
0x3E45, 0x3E4A, 0x3E50, 0x3E56, 0x3E5C, 0x3E61, 0x3E67, 0x3E6C,
0x3E72, 0x3E77, 0x3E7D, 0x3E82, 0x3E88, 0x3E8D, 0x3E92, 0x3E98,
0x3E9D, 0x3EA2, 0x3EA7, 0x3EAC, 0x3EB1, 0x3EB6, 0x3EBB, 0x3EC0,
0x3EC5, 0x3ECA, 0x3ECF, 0x3ED4, 0x3ED8, 0x3EDD, 0x3EE2, 0x3EE7,
0x3EEB, 0x3EF0, 0x3EF4, 0x3EF9, 0x3EFD, 0x3F02, 0x3F06, 0x3F0A,
0x3F0F, 0x3F13, 0x3F17, 0x3F1C, 0x3F20, 0x3F24, 0x3F28, 0x3F2C,
0x3F30, 0x3F34, 0x3F38, 0x3F3C, 0x3F40, 0x3F43, 0x3F47, 0x3F4B,
0x3F4F, 0x3F52, 0x3F56, 0x3F5A, 0x3F5D, 0x3F61, 0x3F64, 0x3F68,
0x3F6B, 0x3F6E, 0x3F72, 0x3F75, 0x3F78, 0x3F7B, 0x3F7F, 0x3F82,
0x3F85, 0x3F88, 0x3F8B, 0x3F8E, 0x3F91, 0x3F94, 0x3F97, 0x3F99,
0x3F9C, 0x3F9F, 0x3FA2, 0x3FA4, 0x3FA7, 0x3FAA, 0x3FAC, 0x3FAF,
0x3FB1, 0x3FB4, 0x3FB6, 0x3FB8, 0x3FBB, 0x3FBD, 0x3FBF, 0x3FC1,
0x3FC4, 0x3FC6, 0x3FC8, 0x3FCA, 0x3FCC, 0x3FCE, 0x3FD0, 0x3FD2,
0x3FD4, 0x3FD5, 0x3FD7, 0x3FD9, 0x3FDB, 0x3FDC, 0x3FDE, 0x3FE0,
0x3FE1, 0x3FE3, 0x3FE4, 0x3FE6, 0x3FE7, 0x3FE8, 0x3FEA, 0x3FEB,
0x3FEC, 0x3FED, 0x3FEF, 0x3FF0, 0x3FF1, 0x3FF2, 0x3FF3, 0x3FF4,
0x3FF5, 0x3FF6, 0x3FF7, 0x3FF7, 0x3FF8, 0x3FF9, 0x3FFA, 0x3FFA,
0x3FFB, 0x3FFC, 0x3FFC, 0x3FFD, 0x3FFD, 0x3FFE, 0x3FFE, 0x3FFE,
0x3FFF, 0x3FFF, 0x3FFF, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000,
0x4000, 0x4000,
};
int __fastcall phd_atan(int x, int y) {
int result;
int swapBuf;
int flags = 0;
if( x == 0 && y == 0 )
return 0;
if( x < 0 ) {
flags |= 4;
x = -x;
}
if( y < 0 ) {
flags |= 2;
y = -y;
}
if( y > x ) {
flags |= 1;
SWAP(x, y, swapBuf);
}
result = AtanBaseTable[flags] + AtanAngleTable[0x800*y/x];
if ( result < 0 )
result = -result;
return result;
}
int __fastcall phd_cos(__int16 angle) {
return phd_sin(angle + PHD_90);
}
int __fastcall phd_sin(__int16 angle) {
UINT16 sector = (UINT16)angle & (PHD_180-1); // sector range will be: 0..0x7FFF (0..179.99 degrees)
if( sector > PHD_90 )
sector = PHD_180 - sector; // sector range will be: 0x0000..0x4000 (0..90 degrees)
int result = PhdSinTable[sector/16];
if( (UINT16)angle >= PHD_180 )
result = -result;
return result;
}
DWORD __fastcall phd_sqrt(DWORD n) {
DWORD result = 0;
DWORD base = 0x40000000;
DWORD basedResult;
for( ; base != 0; base >>= 2 ) {
for( ; base != 0; base >>= 2 ) {
basedResult = base + result;
result >>= 1;
if( basedResult > n ) {
break;
}
n -= basedResult;
result |= base;
}
}
return result;
}
/*
* Inject function
*/
void Inject_PhdMath() {
INJECT(0x00457EA0, phd_atan);
INJECT(0x00457EE8, phd_cos);
INJECT(0x00457EEE, phd_sin);
INJECT(0x00457F23, phd_sqrt);
}
================================================
FILE: 3dsystem/phd_math.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef PHD_MATH_H_INCLUDED
#define PHD_MATH_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
int __fastcall phd_atan(int x, int y); // 0x00457EA0
int __fastcall phd_cos(__int16 angle); // 0x00457EE8
int __fastcall phd_sin(__int16 angle); // 0x00457EEE
DWORD __fastcall phd_sqrt(DWORD n); // 0x00457F23
#endif // PHD_MATH_H_INCLUDED
================================================
FILE: 3dsystem/scalespr.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "3dsystem/scalespr.h"
#include "specific/output.h"
#include "global/vars.h"
#ifdef FEATURE_VIEW_IMPROVED
extern int CalculateFogShade(int depth);
#endif // FEATURE_VIEW_IMPROVED
#ifdef FEATURE_VIDEOFX_IMPROVED
DWORD PickupItemMode = 1;
#endif // FEATURE_VIDEOFX_IMPROVED
void __cdecl S_DrawSprite(DWORD flags, int x, int y, int z, __int16 spriteIdx, __int16 shade, __int16 scale) {
int xv, yv, zv, zp, depth;
int x1, y1, x2, y2;
if( CHK_ANY(flags, SPR_ABS) ) { // absolute coords
x -= MatrixW2V._03;
y -= MatrixW2V._13;
z -= MatrixW2V._23;
if( x < -PhdViewDistance || x > PhdViewDistance )
return;
if( y < -PhdViewDistance || y > PhdViewDistance)
return;
if( z < -PhdViewDistance || z > PhdViewDistance)
return;
zv = MatrixW2V._20 * x + MatrixW2V._21 * y + MatrixW2V._22 * z;
if( zv < PhdNearZ || zv >= PhdFarZ )
return;
yv = MatrixW2V._10 * x + MatrixW2V._11 * y + MatrixW2V._12 * z;
xv = MatrixW2V._00 * x + MatrixW2V._01 * y + MatrixW2V._02 * z;
} else if( (x|y|z) == 0 ) { // zero point coords
zv = PhdMatrixPtr->_23;
if( zv < PhdNearZ || zv > PhdFarZ )
return;
yv = PhdMatrixPtr->_13;
xv = PhdMatrixPtr->_03;
} else { // relative coords
zv = PhdMatrixPtr->_20 * x + PhdMatrixPtr->_21 * y + PhdMatrixPtr->_22 * z + PhdMatrixPtr->_23;
if( zv < PhdNearZ || zv > PhdFarZ )
return;
yv = PhdMatrixPtr->_10 * x + PhdMatrixPtr->_11 * y + PhdMatrixPtr->_12 * z + PhdMatrixPtr->_13;
xv = PhdMatrixPtr->_00 * x + PhdMatrixPtr->_01 * y + PhdMatrixPtr->_02 * z + PhdMatrixPtr->_03;
}
x1 = PhdSpriteInfo[spriteIdx].x1;
y1 = PhdSpriteInfo[spriteIdx].y1;
x2 = PhdSpriteInfo[spriteIdx].x2;
y2 = PhdSpriteInfo[spriteIdx].y2;
#ifdef FEATURE_VIDEOFX_IMPROVED
if( PickupItemMode == 1 && CHK_ALL(flags, SPR_ITEM|SPR_ABS) ) {
if( y1 < y2 ) {
y1 -= y2;
y2 = 0;
} else {
y2 -= y1;
y1 = 0;
}
}
#endif // FEATURE_VIDEOFX_IMPROVED
if( CHK_ANY(flags, SPR_SCALE) ) { // scaling required
x1 = (x1 * scale) << (W2V_SHIFT - 8);
y1 = (y1 * scale) << (W2V_SHIFT - 8);
x2 = (x2 * scale) << (W2V_SHIFT - 8);
y2 = (y2 * scale) << (W2V_SHIFT - 8);
} else { // default scale
x1 <<= W2V_SHIFT;
y1 <<= W2V_SHIFT;
x2 <<= W2V_SHIFT;
y2 <<= W2V_SHIFT;
}
zp = zv / PhdPersp;
x1 = (x1 + xv) / zp + PhdWinCenterX;
if( x1 >= PhdWinWidth )
return;
y1 = (y1 + yv) / zp + PhdWinCenterY;
if( y1 >= PhdWinHeight )
return;
x2 = (x2 + xv) / zp + PhdWinCenterX;
if( x2 < 0 )
return;
y2 = (y2 + yv) / zp + PhdWinCenterY;
if( y2 < 0 )
return;
if( CHK_ANY(flags, SPR_SHADE) ) { // shading required
depth = zv >> W2V_SHIFT;
#ifdef FEATURE_VIEW_IMPROVED
if( depth > PhdViewDistance )
return;
shade += CalculateFogShade(depth);
CLAMP(shade, 0, 0x1FFF);
#else // !FEATURE_VIEW_IMPROVED
if( depth > DEPTHQ_START ) {
shade += depth - DEPTHQ_START;
if( shade > 0x1FFF ) {
return;
}
}
#endif // FEATURE_VIEW_IMPROVED
} else {
shade = 0x1000;
}
#ifdef FEATURE_VIDEOFX_IMPROVED
if( CHK_ANY(flags, SPR_TINT) && !CHK_ANY(flags, SPR_SHADE) ) {
// NOTE: PS1 tint color brightness must be multiplied by 2 in this case
shade = (shade > 0x1000) ? (shade-0x1000)*2 : 0;
}
ins_sprite(zv, x1, y1, x2, y2, spriteIdx, shade, flags);
#else // FEATURE_VIDEOFX_IMPROVED
ins_sprite(zv, x1, y1, x2, y2, spriteIdx, shade);
#endif // FEATURE_VIDEOFX_IMPROVED
}
void __cdecl S_DrawPickup(int sx, int sy, int scale, __int16 spriteIdx, __int16 shade) {
int x1, x2, y1, y2;
#ifdef FEATURE_HUD_IMPROVED
scale = GetRenderScale(scale);
#endif // FEATURE_HUD_IMPROVED
x1 = sx + (PhdSpriteInfo[spriteIdx].x1 * scale / PHD_ONE);
x2 = sx + (PhdSpriteInfo[spriteIdx].x2 * scale / PHD_ONE);
y1 = sy + (PhdSpriteInfo[spriteIdx].y1 * scale / PHD_ONE);
y2 = sy + (PhdSpriteInfo[spriteIdx].y2 * scale / PHD_ONE);
if( x2 >= 0 && y2 >= 0 && x1 < PhdWinWidth && y1 < PhdWinHeight ) {
#ifdef FEATURE_VIDEOFX_IMPROVED
ins_sprite(200, x1, y1, x2, y2, spriteIdx, shade, 0);
#else // FEATURE_VIDEOFX_IMPROVED
ins_sprite(200, x1, y1, x2, y2, spriteIdx, shade);
#endif // FEATURE_VIDEOFX_IMPROVED
}
}
__int16 *__cdecl ins_room_sprite(__int16 *ptrObj, int vtxCount) {
PHD_VBUF *vbuf;
PHD_SPRITE *sprite;
double zp;
int x1, y1, x2, y2;
for( int i = 0; i < vtxCount; ++i ) {
vbuf = &PhdVBuf[*ptrObj];
if( (vbuf->clip & 0x80) == 0 ) {
sprite = &PhdSpriteInfo[ptrObj[1]];
zp = (double)vbuf->zv / (double)PhdPersp;
x1 = (int)((vbuf->xv + (double)((int)sprite->x1 << W2V_SHIFT)) / zp) + PhdWinCenterX;
y1 = (int)((vbuf->yv + (double)((int)sprite->y1 << W2V_SHIFT)) / zp) + PhdWinCenterY;
x2 = (int)((vbuf->xv + (double)((int)sprite->x2 << W2V_SHIFT)) / zp) + PhdWinCenterX;
y2 = (int)((vbuf->yv + (double)((int)sprite->y2 << W2V_SHIFT)) / zp) + PhdWinCenterY;
if( x2 >= PhdWinLeft && y2 >= PhdWinTop && x1 < PhdWinRight && y1 < PhdWinBottom ) {
#ifdef FEATURE_VIDEOFX_IMPROVED
ins_sprite((int)vbuf->zv, x1, y1, x2, y2, ptrObj[1], vbuf->g, 0);
#else // FEATURE_VIDEOFX_IMPROVED
ins_sprite((int)vbuf->zv, x1, y1, x2, y2, ptrObj[1], vbuf->g);
#endif // FEATURE_VIDEOFX_IMPROVED
}
}
ptrObj += 2;
}
return ptrObj;
}
void __cdecl S_DrawScreenSprite2d(int sx, int sy, int sz, int scaleH, int scaleV, __int16 spriteIdx, __int16 shade, UINT16 flags) {
int x1, y1, x2, y2, z;
PHD_SPRITE *sprite = &PhdSpriteInfo[spriteIdx];
x1 = sx + sprite->x1 * scaleH / PHD_ONE;
y1 = sy + sprite->y1 * scaleV / PHD_ONE;
x2 = sx + sprite->x2 * scaleH / PHD_ONE;
y2 = sy + sprite->y2 * scaleV / PHD_ONE;
z = PhdNearZ + sz * 8;
if( x2 >= 0 && y2 >= 0 && x1 < PhdWinWidth && y1 < PhdWinHeight ) {
#ifdef FEATURE_VIDEOFX_IMPROVED
ins_sprite(z, x1, y1, x2, y2, spriteIdx, shade, 0);
#else // FEATURE_VIDEOFX_IMPROVED
ins_sprite(z, x1, y1, x2, y2, spriteIdx, shade);
#endif // FEATURE_VIDEOFX_IMPROVED
}
}
void __cdecl S_DrawScreenSprite(int sx, int sy, int sz, int scaleH, int scaleV, __int16 spriteIdx, __int16 shade, UINT16 flags) {
int x1, y1, x2, y2, z;
PHD_SPRITE *sprite = &PhdSpriteInfo[spriteIdx];
x1 = sx + (sprite->x1 / 8) * scaleH / PHD_ONE;
y1 = sy + (sprite->y1 / 8) * scaleV / PHD_ONE;
x2 = sx + (sprite->x2 / 8) * scaleH / PHD_ONE;
y2 = sy + (sprite->y2 / 8) * scaleV / PHD_ONE;
z = sz * 8;
if( x2 >= 0 && y2 >= 0 && x1 < PhdWinWidth && y1 < PhdWinHeight ) {
#ifdef FEATURE_VIDEOFX_IMPROVED
ins_sprite(z, x1, y1, x2, y2, spriteIdx, shade, 0);
#else // FEATURE_VIDEOFX_IMPROVED
ins_sprite(z, x1, y1, x2, y2, spriteIdx, shade);
#endif // FEATURE_VIDEOFX_IMPROVED
}
}
void __cdecl draw_scaled_spriteC(__int16 *ptrObj) {
#ifdef FEATURE_NOLEGACY_OPTIONS
extern int GetPitchSWR();
int pitch = GetPitchSWR();
#else // FEATURE_NOLEGACY_OPTIONS
int pitch = PhdScreenWidth; // NOTE: this is the original game bug!
#endif // FEATURE_NOLEGACY_OPTIONS
int i, j;
int x1, y1, x2, y2, width, height;
int u, uBase, vBase, uAdd, vAdd;
__int16 sprIdx, shade;
BYTE *srcBase, *src, *dst;
BYTE pix;
int dstAdd;
bool isDepthQ;
PHD_SPRITE *sprite;
DEPTHQ_ENTRY *depthQ;
x1 = ptrObj[0];
y1 = ptrObj[1];
x2 = ptrObj[2];
y2 = ptrObj[3];
sprIdx = ptrObj[4];
shade = ptrObj[5];
if( x1 >= x2 || y1 >= y2 || x2 <= 0 || y2 <= 0 || x1 >= PhdWinMaxX || y1 >= PhdWinMaxY )
return;
sprite = &PhdSpriteInfo[sprIdx];
depthQ = &DepthQTable[shade >> 8];
uBase = vBase = 0x4000;
uAdd = (sprite->width - 64) * 256 / (x2 - x1);
vAdd = (sprite->height - 64) * 256 / (y2 - y1);
if( x1 < 0 ) {
uBase -= uAdd * x1;
x1 = 0;
}
if( y1 < 0 ) {
vBase -= vAdd * y1;
y1 = 0;
}
CLAMPG(x2, PhdWinMaxX + 1);
CLAMPG(y2, PhdWinMaxY + 1);
width = x2 - x1;
height = y2 - y1;
srcBase = (BYTE *)TexturePageBuffer8[sprite->texPage] + sprite->offset;
dst = PrintSurfacePtr + (PhdWinMinY + y1) * pitch + (PhdWinMinX + x1);
dstAdd = pitch - width;
#if (DIRECT3D_VERSION >= 0x900)
isDepthQ = (depthQ != &DepthQTable[15]);
#else // (DIRECT3D_VERSION >= 0x900)
isDepthQ = (GameVid_IsWindowedVga || depthQ != &DepthQTable[15]); // NOTE: index was 16 in the original code, this was wrong
#endif // (DIRECT3D_VERSION >= 0x900)
for( i = 0; i < height; ++i ) {
u = uBase;
src = srcBase + (vBase >> 16) * 256;
for( j = 0; j < width; ++j ) {
pix = src[u >> 16];
if( pix != 0 ) {
*dst = ( isDepthQ ) ? depthQ->index[pix] : pix;
}
u += uAdd;
++dst;
}
dst += dstAdd;
vBase += vAdd;
}
}
/*
* Inject function
*/
void Inject_ScaleSpr() {
INJECT(0x0040C030, S_DrawSprite);
INJECT(0x0040C300, S_DrawPickup);
INJECT(0x0040C390, ins_room_sprite);
INJECT(0x0040C4F0, S_DrawScreenSprite2d);
INJECT(0x0040C590, S_DrawScreenSprite);
INJECT(0x0040C630, draw_scaled_spriteC);
}
================================================
FILE: 3dsystem/scalespr.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef SCALESPR_H_INCLUDED
#define SCALESPR_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl S_DrawSprite(DWORD flags, int x, int y, int z, __int16 spriteIdx, __int16 shade, __int16 scale); // 0x0040C030
void __cdecl S_DrawPickup(int sx, int sy, int scale, __int16 spriteIdx, __int16 shade); // 0x0040C300
__int16 *__cdecl ins_room_sprite(__int16 *ptrObj, int vtxCount); // 0x0040C390
void __cdecl S_DrawScreenSprite2d(int sx, int sy, int sz, int scaleH, int scaleV, __int16 spriteIdx, __int16 shade, UINT16 flags); // 0x0040C4F0
void __cdecl S_DrawScreenSprite(int sx, int sy, int sz, int scaleH, int scaleV, __int16 spriteIdx, __int16 shade, UINT16 flags); // 0x0040C590
void __cdecl draw_scaled_spriteC(__int16 *ptrObj); // 0x0040C630
#endif // SCALESPR_H_INCLUDED
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.9.0] - 2023-06-05
### New features
- The Golden Mask game is included into a release pack, since it's freeware. The original game level files must be officially obtained via Steam/GOG/CD/whatever.
- Added DirectX 9 support. However, TR2Main can be compiled for DirectX 5 too.
- Into TR2Main.dll added information about the current version. Now it will be easier to find out which DLL is which version.
- The number of secrets in the script is taken into account when calculating the total number of secrets in the final statistics. Now it is calculated correctly, regardless of the total number of levels, bonus levels, and even mod levels.
- Additional checks have been implemented to prevent Lara from getting weapons that are not supported by current level. Thus, a number of problems associated with the "Nightmare in Vegas" level are fixed.
- Now *"New Game+"* is activated after the completion of the latest level in the script, and not any in which the final statistics appear. This allows you to activate this mode after the "Nightmare in Vegas" level, and not before.
- Now you can start the game in Gold mode, specifying the option *"-gold"* on the command line or shortcut properties (in the same way as the *"-setup"* option).
- For the Gold mode, Spider and Big Spider AI replaced by Wolf and Bear AI. Also Monk Spirits have no shadow.
- For the Gold mode, the credits slideshow starts from *CREDIT00.PCX*. For the original game mode, it starts from *CREDIT01.PCX*.
- For the Gold mode, the selection of any levels (including bonus levels) is activated after collecting all the secrets (available before the final statistics). The ability to select levels remains even after relaunching the game.
- For the Gold mode, pictures with "g" filename suffix (TITLEg.PCX, LEGALg.PCX, etc.) have higher priority than pictures without "g" filename suffix (TITLE.PCX, LEGAL.PCX, etc.)
- For the Gold mode, instead of the files *"TOMBPC.DAT"* and *"MAIN.SFX"*, the files *"TOMBPCg.DAT"* and *"MAINg.SFX"* are used. However, for both Gold and the original game, *"MAIN.SFX"* is still used for the *"TITLE.TR2"* and *"ASSAULT.TR2"* levels.
- Added separate subfolders for saved games of Gold and the original game.
- Added PlayStation reflection visual effect.
- Z-Buffer is no longer required for PlayStation styled gradient health/air bars.
- Added basic/advanced alpha blending for PlayStation styled semitransparent effects.
- Added glow effect for gunfire/flare (alpha blending required).
- Most sprites, gunfire/flare effects, sunglasses lens, the dragon spheres of doom are made semitransparent (alpha blending required). This is how they look in the PlayStation version.
- Rolling wheel blades, wall mounted blades, swords of statues and flamethrower masks are made reflective. This is how they look in the PlayStation version.
- The armed snowmobile windshield is made reflective. This is not presented in the PlayStation version, but probably it was supposed to be. Anyway it looks cool!
- The original game fast snowmobile windshield is made semitransparent (alpha blending required). This is how it looks in the PlayStation version.
- The Golden Mask fast snowmobile windshield is made reflective. In the Golden Mask it has the same windshield model as the armed snowmobile.
- Boathouse Key and Library Key from Venice levels are made reflective. This is how they look in the PlayStation version.
- The water surfaces and the glass on the sink in Lara's bathroom are made semitransparent (alpha blending required). This is how they look in the PlayStation version.
- Chain-link fence texture from Venice levels and stained glass texture from "Wreck of the Maria Doria" level are made semitransparent (alpha blending required). This is how they look in the PlayStation version.
- The glass on the sink in the "Nightmare in Vegas" level is made semitransparent (alpha blending required).
- Added support for loading screens from the script.
- Added TR2Main.json configuration file for easy game modding in the future.
- Added support for loading screens from TR2Main.json configuration file.
- Added support for custom water color from TR2Main.json configuration file.
- Added support for barefoot steps SFX from TR2Main.json configuration file (BAREFOOT.SFX in the DATA folder required).
- Added support for Legal/Title pictures with both US/EU logo (just a simple option to toggle between logo versions).
- Remastered pictures (PNG/JPG/BMP) support can be disabled now. In this case the game will use only PCX pictures.
- Custom HUD scale can be set to any value in 0.5 - 2.0 (previously it was limited by 1.0 - 2.0).
- Added support for display resolutions higher than 2048x2048.
- Background picture code is redesigned to support resolutions higher than 2048x2048.
- The limitations of the game engine (textures, polygons) are expanded by 4 times for future TR2 mods.
- Added music mute settings for inventory/underwater.
- The music and cut scenes are paused when the game window is not active.
- Added options for level statistics background mode (hardware renderer required).
- Added full inventory cheat on *I* key (cheat flag in *"TOMBPC.DAT"* file required). It gives full health, all weapons, ammo, medipacks, flares and all keys.
- Added Dozy cheat on *O* key (cheat flag in *"TOMBPC.DAT"* file required). It gives fly mode and full health, use *Shift* key to disable it.
- Lara has golden skin while in Dozy mode (hardware renderer and reflection feature required).
- SFX number limit increased from 256 to 370.
- Added automatic cdaudio.dat fix for PaulD's CD Audio solution, if some track has wrong parameters (like in the Steam version).
- Added possibility to customize semitransparent/reflective polygons via TR2Main.json configuration file.
- Added the ability to side step hitting the *Left*/*Right* key while holding *Walk* key (optional feature).
- Added possibility to hold up/down keys to quickly scroll through game menus.
- Added a configurable number of saved game slots (16-24).
- Joystick support completely migrated from WinAPI to DirectInput. Added XInput controllers support. Added DualShock 4 / DualSense support.
- Joystick controls redesigned to use thumb sticks and D-Pad at the same time.
- Added vibration feedback support for XInput gamepads and DualShock 4 / DualSense.
- Added the light bar color feedback support for DualShock 4 / DualSense. The color changes depending from health and oxygen.
- Passport text box height is adjusted depending from HUD scale.
- Some legacy options are removed (Triple Buffer, Dither, Perspective Correction, Texel Adjustment, Disable 16 bit textures, Don't sort transparent polys).
- Screen resolution settings reworked, now the game automatically selects between True Color / High Color.
- Now the game correctly keeps proper resolutions when it switches between Windowed / Full Screen.
- Implemented embedded textures for Keyboard/Joystick button sprites. There are 3 styles for Joystick buttons: Numeric, XBox, PlayStation.
- Added arrows to PlayStation styled inventory text boxes.
- Sound/Music volumes presented as gradient bars if PlayStation styled inventory text boxes are enabled. Also there are PlayStation styled SFX for changing volume.
- Added PlayStation styled Joystick button hints for inventory (optional feature).
- Added Pause feature (P key). Two styles are available: transparent and 50% darkened.
- Controls text box completely redesigned. Now you can view both default and custom keyboard layouts on the same page. Joystick layout is now independent from the custom keyboard layout.
- Fade out to black implemented for levels, demos, cut scenes and statistics.
- Added an option to select Demo Mode text style: none, PC or PlayStation.
- Now the inventory stopwatch (statistics item) position depends from HUD scale and Field of View options. So it looks both PlayStation and PC authentic.
- The message is displayed when Bilinear Filter or Z-Buffer is toggled with F7/F8 hotkey (DirectX 9 only).
- Added high contrast lighting (PlayStation style) for Hardware Renderer (DirectX 9 only).
- Added low contrast lighting (Classic PC Hardware style) for Software Renderer.
- Added F11 hotkey to toggle low/high contrast lighting (in the original game F11 was used to toggle Dither).
- Sunset effect in Bartoli's Hideout now makes skybox a little darker.
- Added "Restart Level" option (available only in case of Lara's death).
- The inventory pattern (both static and animated) is seamless now for Bilinear Filter.
- Added external HD textures support (DirectX 9 only).
- Added iOS/Android texture pack full support (DirectX 9 only).
- Added ffmpeg based FMV player.
- Added loading screens by Lito Perezito for The Golden Mask.
### The original game bugfixes
- Fixed a bug that prevented the display of the save counter until the game relaunch, if the game was saved in an empty slot.
- Fixed a bug that allowed the player to interfere with the control of the demo level.
- Demo levels are excluded from the *"New Game"* menu, if the *"Select Level"* option is active.
- Fixed the statistics reset in case of *"New Game+"*, if the *"Select Level"* option is active.
- Fixed a bug that does not happen in the official levels (this situation is not in the script). Suppose a shotgun and shotgun ammo are set as a bonus for collecting all the secrets in the level. If Lara does not have a shotgun at the moment of picking the last secret, she receives the relying bonus, but the ammo items are not displayed on the screen. This is fixed now.
- Fixed a bug that does not happen in the official levels (this situation is not in the script). Suppose a magnum and magnum ammo are set as a bonus for collecting all the secrets in the level. If Lara does not have a magnum at the moment of picking the last secret, she receives the magnum only, but not the magnum ammo. This is fixed now.
- Fixed a bug that hid the ammo indicator in demo levels if *"New Game+"* is activated.
- Fixed a bug that produced black triangles originating from the top left of the screen on some laptop graphic adapters.
- Fixed a bug that produced traces of previous textures in untextured areas.
- If "Disable 16 bit textures" option is unsupported, it's disabled automatically instead of breaking the textures.
- Fixed a bug that changed the music volume incorrectly when leaving inventory while the camera is underwater.
- Fixed a rare game crash occurring when you explode an enemy with a grenade launcher.
- Fixed incorrect sorting of 3D polygons if Z-Buffer is turned off or the software renderer is used.
- Fixed a bug due to which the skybox was not drawn in some rooms.
- Fixed a bug where pickup sprites were cropped by the floor if Z-Buffer is enabled. Some pickup items are slightly raised (optional fix).
- Fixed a bug due to which the last pressed key was accidentally assigned when redefining keys.
- "Run Control Panel" button in the setup dialog now opens the current DirectInput joystick configuration from Control Panel instead of Control Panel itself.
- Fixed a bug that caused the harpoon gun to reload every single shot in *"New Game+"* after a weapon cheat was used.
- Fixed a bug due to which none of the joystick buttons allowed returning to the previous menu. Now the button assigned to "Draw weapon" is used for this.
- Fixed a bug due to which the accuracy of the M16 did not decrease while running (optional fix).
- Fixed a wrong inventory text position if the game window size is changed.
- Fixed a flashing "Demo Mode" text while demo is playing.
- The *low ceiling jump* bugfix is optional now.
- Fixed a bug due to which non-textured surfaces could appear brighter than textured ones with the same lighting settings.
- Fixed a bug that reset timer of sunset effect in Bartoli's Hideout after saved game is loaded.
### TR2Main bugfixes
- Background capture is optimized even more, now it is 3-4 times faster than v0.8.2. No lags anymore (broken since v0.8.0).
- Fixed game crash in Software Rendered mode with PlayStation styled inventory text box enabled (broken since v0.8.0).
- Fixed a rare game crash occurring when launching the game (broken since v0.1.0).
- Fixed sunset effect in Bartoli's Hideout level (broken since v0.1.0).
- Fixed texel adjustment if the filtering is changed by *F8* hotkey while "Adjust when bilinear filtering" option is enabled (broken since v0.1.0).
- Now the PaulD's CD Audio is paused when the music volume is set to 0. This is how real CD Audio works (broken since v0.2.0).
- Fixed "Gosh! That was my best time yet!" phrase in the case if it's a first try after game launch, but the time is not the best at all (broken since v0.5.0).
- Fixed a bug when some underwater objects were untinted (broken since v0.1.0).
- Fixed camera stabilization in some cut scenes (broken since v0.1.0).
- Fixed the "Microphone Position at Lara" setting (broken since v0.1.0).
- Changed randomizer formula to be less correct, but consistent with the original game (broken since v0.1.0).
## [0.8.2] - 2019-05-26
### TR2Main bugfixes
- Background capture is redesigned, now it's fast again (broken since v0.8.0).
- Background capture is processed only when required (broken since v0.8.0).
- Fixed PlayStation styled field of view (broken since v0.8.0).
## [0.8.1] - 2018-10-21
### The original game bugfixes
- Fixed texture corruption after FMV scenes.
- Fixed the game hangup if the game window is closed on the statistics screen.
### TR2Main bugfixes
- Level statistics background properly fades out now (broken since v0.8.0).
## [0.8.0] - 2018-09-25
### New features
- Added automatic blocking of incompatible video modes that caused the game to crash.
- The resolution limit is set to 2048x2048, since DirectX 5 cannot handle more.
- Changed the default aspect ratio from 1:1 to 4:3 for the windowed mode with the "any aspect" in the setup dialog.
- Added possibility to use up to 99 images in the credits slideshow. The original game limit is 9 images.
- Added support for HD PCX images (up to 2048x2048). The original game supported only 640x480 images.
- Added stretch limit control for background picture (optional feature).
- Added PNG/JPEG/BMP images support (hardware renderer required).
- Added *PIX* folder for automatic management of pictures with different aspect ratios.
- Added PNG screenshots with date-time filename format (optional feature). TGA/PCX screenshots retained the sequential filename format.
- Added setting to specify a user directory for saving screenshots files.
- Added edge padding instead of UV adjustment for background textures.
- Now *"end"* picture is used for the final statistics background.
- Added fade in/out between scenes.
- Added TR1 styled transparent inventory background for hardware renderer (optional feature). This background is also used for level statistics like in the PlayStation version even if not set as inventory background.
- Text box requester line height is slightly reduced to be more compact.
- Added PlayStation styled inventory text box (optional feature).
### The original game bugfixes
- Fixed the original game bug when the health bar is visible at the beginning of the final bath scene.
- Screenshots now work properly in the windowed mode in modern Windows systems.
- Screen tearing does not affect screenshots for the windowed mode anymore.
- Fixed an issue where the final bath scene is cut off. Now it fades out like in the PlayStation version.
- Text box frame is slightly adjusted to fill gaps in the corners.
### TR2Main bugfixes
- Fixed incorrect underwater fog settings (broken since v0.4.0).
- Fixed a minor issue related with clipping of round shadows (broken since v0.7.0).
- Fixed clipping of animated background textures (broken since v0.3.0).
## [0.7.0] - 2018-07-28
### New features
- Added round shadows instead of octagonal (optional feature).
### The original game bugfixes
- Fixed shadow clipping if the Z-Buffer is turned on.
- Fixed a rare bug that caused black sprites to appear in bright rooms.
### TR2Main bugfixes
- Fixed a bug that caused invisibility of pitch black sprites (broken since v0.4.0).
## [0.6.0] - 2018-02-24
### New features
- Custom HUD scaling options separated for the inventory and in-game (optional feature).
### The original game bugfixes
- The size of the active screen area should always be 1.0 for credits and final statistics.
### TR2Main bugfixes
- Health bar coordinates slightly corrected in the inventory for the PlayStation position style (broken since v0.5.0).
## [0.5.0] - 2018-02-17
### New features
- Improved optimization of the perspective correction for the software renderer (up to 2x faster than the original game code)
- Text is unstretched for any display modes.
- Added automatic scaling of text box borders depending on the current screen resolution.
- Added automatic scaling of the assault course timer depending on the current screen resolution.
- The best results of assault course are saved in the registry and are not reset after relaunching the game.
- The health/air bar accuracy increased for higher resolutions.
- Added PlayStation styled health bar position (optional feature).
- Added custom HUD scaling options (optional feature).
- Added simplified gradient health/air bars (optional feature; hardware renderer and Z-Buffer required).
### The original game bugfixes
- Fixed texel adjustment if the renderer is changed by *Shift+F12* hotkey.
- Fixed untextured polygon sorting for the software renderer.
- Other minor bugfixes of the renderer.
### TR2Main bugfixes
- Fixed cheat sequences (broken since v0.1.0).
## [0.4.0] - 2018-01-05
### New features
- Added draw distance and fog settings (optional feature).
### TR2Main bugfixes
- Fixed texture minification filtering (broken since v0.1.0).
- Fixed Z-Buffer for animated background of the inventory (broken since v0.3.0).
- Fixed *"Select level"* option (broken since v0.1.0).
## [0.3.0] - 2018-01-03
### New features
- Added PlayStation styled field of view (optional feature).
- Added PlayStation styled gradient health/air bars (optional feature; hardware renderer and Z-Buffer required).
- Added PlayStation styled (animated) background of the inventory (optional feature; hardware renderer required).
- Tiled static background of the inventory is unstretched for any display modes.
### The original game bugfixes
- Fixed a rare game crash occurring when exiting the game while playing CD Audio.
### TR2Main bugfixes
- Fixed back-face culling if the Z-Buffer is turned on (broken since v0.1.0).
- Fixed a bug that prevented collecting more than one item of each type in the inventory (broken since v0.1.0).
- Fixed a minor bug with looped tracks in the PaulD's CD Audio solution code (broken since v0.2.0).
## [0.2.0] - 2017-12-31
### New features
- The TGA screenshot captures the game window instead of the whole screen in the case of windowed mode.
- Added PaulD's CD Audio solution. Now the game supports *cdaudio.mp3* / *cdaudio.dat* music package. Just put it into *audio* folder in the game installation directory.
### The original game bugfixes
- Fixed default/custom keyboard layout conflicts when starting the game without visiting the controls menu.
### TR2Main bugfixes
- Z-Buffer depth priority reverted to original 16/24/32/8 due to the *"CreateZBuffer"* errors on some systems (broken since v0.1.0).
- Screenshot key is set to *BackSpace* (previously it was *PrintScreen*) to fix problems on some systems (broken since v0.1.0).
- Fixed CD Audio synchronization bug (broken since v0.1.0).
## [0.1.0] - 2017-12-25
### New features
- Added NoCD patch. It is required that the *DATA* and *FMV* folders are located in the game installation folder, and not on the CD.
- Added automatic adjustment of aspect ratio and field of view, depending on the current screen resolution.
- Added automatic scaling of picked up items depending on the current screen resolution.
- Added automatic scaling of secrets (dragons) in the level statistics menu depending on the current screen resolution.
- Added automatic scaling of the health/air bars depending on the current screen resolution.
- Video modes in the setup dialog are now sorted by depth, width and height.
- Screenshot files are now created in the *screenshots* subfolder, not the game installation folder itself.
- TGA screenshots are now available for 24/32-bit video modes. Not just for 16-bit video modes, as it was in the original game.
- Screenshot key is set to *PrintScreen*. In the original game, it's set to the *S* key, which makes it unacceptable to map *S* key with another action.
- ~~Z-Buffer depth priority set to 32/24/16/8 bits to improve rendering quality if a 32-bit Z-Buffer is available. In the original game, it is 16/24/32/8~~ (reverted in v0.2.0).
### The original game bugfixes
- Fixed incorrect parameters of the game window, leading to the appearance of unwanted borders in full screen mode.
- The default music volume level is set to 10/10. In the original game, the default value is 165/10, which leads to the absence of music until the volume level is adjusted in the game menu.
- Fixed the *low ceiling jump* bug that was present in the early releases of the Tomb Raider II. In later versions, Core Design fixed it.
- Fixed a bug that caused some graphic objects to disappear or flicker if the Z-Buffer is turned off.
- Fixed a bug leading to a non-responsive keyboard when switching to another Windows application or launching a game using Wine under Linux.
- Removed repeating actions caused by a single hotkey keystroke (F1, F2, F12, Screenshot key). One keystroke - one action!
- Fixed the problem when the game overwrites the screenshot files made in previous launches of the game.
- Fixed unsafe memory management in the screenshot function, which caused the game to crash at higher screen resolutions.
- Fixed incorrect TGA screenshot creation. In the original game, the bottom line of pixels is filled with junk.
[Unreleased]: https://github.com/Arsunt/TR2Main/compare/v0.9.0...HEAD
[0.9.0]: https://github.com/Arsunt/TR2Main/compare/v0.8.2...v0.9.0
[0.8.2]: https://github.com/Arsunt/TR2Main/compare/v0.8.1...v0.8.2
[0.8.1]: https://github.com/Arsunt/TR2Main/compare/v0.8.0...v0.8.1
[0.8.0]: https://github.com/Arsunt/TR2Main/compare/v0.7.0...v0.8.0
[0.7.0]: https://github.com/Arsunt/TR2Main/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/Arsunt/TR2Main/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/Arsunt/TR2Main/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/Arsunt/TR2Main/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/Arsunt/TR2Main/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/Arsunt/TR2Main/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/Arsunt/TR2Main/releases/tag/v0.1.0
================================================
FILE: COPYING.md
================================================
GNU GENERAL PUBLIC LICENSE
==========================
Version 3, 29 June 2007
==========================
> Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
# Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
# TERMS AND CONDITIONS
## 0. Definitions.
_"This License"_ refers to version 3 of the GNU General Public License.
_"Copyright"_ also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
_"The Program"_ refers to any copyrightable work licensed under this
License. Each licensee is addressed as _"you"_. _"Licensees"_ and
"recipients" may be individuals or organizations.
To _"modify"_ a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a _"modified version"_ of the
earlier work or a work _"based on"_ the earlier work.
A _"covered work"_ means either the unmodified Program or a work based
on the Program.
To _"propagate"_ a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To _"convey"_ a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
## 1. Source Code.
The _"source code"_ for a work means the preferred form of the work
for making modifications to it. _"Object code"_ means any non-source
form of a work.
A _"Standard Interface"_ means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The _"System Libraries"_ of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The _"Corresponding Source"_ for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
## 2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
## 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
## 4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
## 5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
## 6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A _"User Product"_ is either (1) a _"consumer product"_, which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
_"Installation Information"_ for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
## 7. Additional Terms.
_"Additional permissions"_ are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
## 8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
## 9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
## 10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An _"entity transaction"_ is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
## 11. Patents.
A _"contributor"_ is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's _"essential patent claims"_ are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
## 12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
## 13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
## 14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
## 15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
## 16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
## 17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
# END OF TERMS AND CONDITIONS
--------------------------------------------------------------------------
# How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type 'show c' for details.
The hypothetical commands _'show w'_ and _'show c'_ should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
================================================
FILE: Doxyfile
================================================
# Doxyfile 1.8.13
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
#
# All text after a double hash (##) is considered a comment and is placed in
# front of the TAG it is preceding.
#
# All text after a single hash (#) is considered a comment and will be ignored.
# The format is:
# TAG = value [value, ...]
# For lists, items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \").
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# This tag specifies the encoding used for all characters in the config file
# that follow. The default is UTF-8 which is also the encoding used for all text
# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
# for the list of possible encodings.
# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
# double-quotes, unless you are using Doxywizard) that should identify the
# project for which the documentation is generated. This name is used in the
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = "TR2Main by Arsunt"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER =
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = "Dynamic Linked Library implementing Tomb Raider 2 graphics"
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
# the logo to the output directory.
PROJECT_LOGO = ./logo.png
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = ./doc
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and
# will distribute the generated files over these directories. Enabling this
# option can be useful when feeding doxygen a huge amount of source files, where
# putting all generated files in the same directory would otherwise causes
# performance problems for the file system.
# The default value is: NO.
CREATE_SUBDIRS = NO
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
# U+3044.
# The default value is: NO.
ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
# Ukrainian and Vietnamese.
# The default value is: English.
OUTPUT_LANGUAGE = English
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
# The default value is: YES.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
# description of a member or function before the detailed description
#
# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
# The default value is: YES.
REPEAT_BRIEF = YES
# This tag implements a quasi-intelligent brief description abbreviator that is
# used to form the text in various listings. Each string in this list, if found
# as the leading text of the brief description, will be stripped from the text
# and the result, after processing the whole list, is used as the annotated
# text. Otherwise, the brief description is used as-is. If left blank, the
# following values are used ($name is automatically replaced with the name of
# the entity):The $name class, The $name widget, The $name file, is, provides,
# specifies, contains, represents, a, an and the.
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
"The $name file" \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# doxygen will generate a detailed section even if there is only a brief
# description.
# The default value is: NO.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
# inherited members of a class in the documentation of that class as if those
# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
# The default value is: NO.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
# before files name in the file list and in the header files. If set to NO the
# shortest path that makes the file name unique will be used
# The default value is: YES.
FULL_PATH_NAMES = YES
# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
# Stripping is only done if one of the specified strings matches the left-hand
# part of the path. The tag can be used to show relative paths in the file list.
# If left blank the directory from which doxygen is run is used as the path to
# strip.
#
# Note that you can specify absolute paths here, but also relative paths, which
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH =
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
# header file to include in order to use a class. If left blank only the name of
# the header file containing the class definition is used. Otherwise one should
# specify the list of include paths that are normally passed to the compiler
# using the -I flag.
STRIP_FROM_INC_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
# less readable) file names. This can be useful is your file systems doesn't
# support long names like on DOS, Mac, or CD-ROM.
# The default value is: NO.
SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
# first line (until the first dot) of a Javadoc-style comment as the brief
# description. If set to NO, the Javadoc-style will behave just like regular Qt-
# style comments (thus requiring an explicit @brief command for a brief
# description.)
# The default value is: NO.
JAVADOC_AUTOBRIEF = NO
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
# requiring an explicit \brief command for a brief description.)
# The default value is: NO.
QT_AUTOBRIEF = NO
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
# a brief description. This used to be the default behavior. The new default is
# to treat a multi-line C++ comment block as a detailed description. Set this
# tag to YES if you prefer the old behavior instead.
#
# Note that setting this tag to YES also means that rational rose comments are
# not recognized any more.
# The default value is: NO.
MULTILINE_CPP_IS_BRIEF = NO
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# The default value is: YES.
INHERIT_DOCS = YES
# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
# page for each member. If set to NO, the documentation of a member will be part
# of the file/class/namespace that contains it.
# The default value is: NO.
SEPARATE_MEMBER_PAGES = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
# uses this value to replace tabs by spaces in code fragments.
# Minimum value: 1, maximum value: 16, default value: 4.
TAB_SIZE = 4
# This tag can be used to specify a number of aliases that act as commands in
# the documentation. An alias has the form:
# name=value
# For example adding
# "sideeffect=@par Side Effects:\n"
# will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading
# "Side Effects:". You can put \n's in the value part of an alias to insert
# newlines.
ALIASES =
# This tag can be used to specify a number of word-keyword mappings (TCL only).
# A mapping has the form "name=value". For example adding "class=itcl::class"
# will allow you to use the command class in the itcl::class meaning.
TCL_SUBST =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
# members will be omitted, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = YES
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
# Python sources only. Doxygen will then generate output that is more tailored
# for that language. For instance, namespaces will be presented as packages,
# qualified scopes will look different, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
# sources. Doxygen will then generate output that is tailored for Fortran.
# The default value is: NO.
OPTIMIZE_FOR_FORTRAN = NO
# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
# sources. Doxygen will then generate output that is tailored for VHDL.
# The default value is: NO.
OPTIMIZE_OUTPUT_VHDL = NO
# Doxygen selects the parser to use depending on the extension of the files it
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
# Fortran. In the later case the parser tries to guess whether the code is fixed
# or free formatted code, this is the default for Fortran type files), VHDL. For
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen.
EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
# documentation. See http://daringfireball.net/projects/markdown/ for details.
# The output of markdown processing is further processed by doxygen, so you can
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
# case of backward compatibilities issues.
# The default value is: YES.
MARKDOWN_SUPPORT = YES
# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 0.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 0
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or
# globally by setting AUTOLINK_SUPPORT to NO.
# The default value is: YES.
AUTOLINK_SUPPORT = YES
# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
# to include (a tag file for) the STL sources as input, then you should set this
# tag to YES in order to let doxygen match functions declarations and
# definitions whose arguments contain STL classes (e.g. func(std::string);
# versus func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
# The default value is: NO.
BUILTIN_STL_SUPPORT = NO
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
# The default value is: NO.
CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
# will parse them like normal C++ but will assume all classes use public instead
# of private inheritance when no explicit protection keyword is present.
# The default value is: NO.
SIP_SUPPORT = NO
# For Microsoft's IDL there are propget and propput attributes to indicate
# getter and setter methods for a property. Setting this option to YES will make
# doxygen to replace the get and set methods by a property in the documentation.
# This will only work if the methods are indeed getting or setting a simple
# type. If this is not the case, or you want to show the methods anyway, you
# should set this option to NO.
# The default value is: YES.
IDL_PROPERTY_SUPPORT = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
# The default value is: NO.
DISTRIBUTE_GROUP_DOC = NO
# If one adds a struct or class to a group and this option is enabled, then also
# any nested class or struct is added to the same group. By default this option
# is disabled and one has to add nested compounds explicitly via \ingroup.
# The default value is: NO.
GROUP_NESTED_COMPOUNDS = NO
# Set the SUBGROUPING tag to YES to allow class member groups of the same type
# (for instance a group of public functions) to be put as a subgroup of that
# type (e.g. under the Public Functions section). Set it to NO to prevent
# subgrouping. Alternatively, this can be done per class using the
# \nosubgrouping command.
# The default value is: YES.
SUBGROUPING = YES
# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
# are shown inside the group in which they are included (e.g. using \ingroup)
# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
# and RTF).
#
# Note that this feature does not work in combination with
# SEPARATE_MEMBER_PAGES.
# The default value is: NO.
INLINE_GROUPED_CLASSES = NO
# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
# with only public data fields or simple typedef fields will be shown inline in
# the documentation of the scope in which they are defined (i.e. file,
# namespace, or group documentation), provided this scope is documented. If set
# to NO, structs, classes, and unions are shown on a separate page (for HTML and
# Man pages) or section (for LaTeX and RTF).
# The default value is: NO.
INLINE_SIMPLE_STRUCTS = NO
# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
# enum is documented as struct, union, or enum with the name of the typedef. So
# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
# with name TypeT. When disabled the typedef will appear as a member of a file,
# namespace, or class. And the struct will be named TypeS. This can typically be
# useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
# The default value is: NO.
TYPEDEF_HIDES_STRUCT = NO
# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
# cache is used to resolve symbols given their name and scope. Since this can be
# an expensive process and often the same symbol appears multiple times in the
# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
# doxygen will become slower. If the cache is too large, memory is wasted. The
# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
# symbols. At the end of a run doxygen will report the cache usage and suggest
# the optimal cache size from a speed point of view.
# Minimum value: 0, maximum value: 9, default value: 0.
LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
# documentation are documented, even if no documentation was available. Private
# class members and static file members will be hidden unless the
# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
# Note: This will also disable the warnings about undocumented members that are
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
EXTRACT_ALL = NO
# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
# be included in the documentation.
# The default value is: NO.
EXTRACT_PRIVATE = NO
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
# The default value is: NO.
EXTRACT_PACKAGE = NO
# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
# included in the documentation.
# The default value is: NO.
EXTRACT_STATIC = NO
# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
# locally in source files will be included in the documentation. If set to NO,
# only classes defined in header files are included. Does not have any effect
# for Java sources.
# The default value is: YES.
EXTRACT_LOCAL_CLASSES = YES
# This flag is only useful for Objective-C code. If set to YES, local methods,
# which are defined in the implementation section but not in the interface are
# included in the documentation. If set to NO, only methods in the interface are
# included.
# The default value is: NO.
EXTRACT_LOCAL_METHODS = NO
# If this flag is set to YES, the members of anonymous namespaces will be
# extracted and appear in the documentation as a namespace called
# 'anonymous_namespace{file}', where file will be replaced with the base name of
# the file that contains the anonymous namespace. By default anonymous namespace
# are hidden.
# The default value is: NO.
EXTRACT_ANON_NSPACES = NO
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation
# section is generated. This option has no effect if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option
# has no effect if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
# (class|struct|union) declarations. If set to NO, these declarations will be
# included in the documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
# documentation blocks found inside the body of a function. If set to NO, these
# blocks will be appended to the function's detailed documentation block.
# The default value is: NO.
HIDE_IN_BODY_DOCS = NO
# The INTERNAL_DOCS tag determines if documentation that is typed after a
# \internal command is included. If the tag is set to NO then the documentation
# will be excluded. Set it to YES to include the internal documentation.
# The default value is: NO.
INTERNAL_DOCS = NO
# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
# names in lower-case letters. If set to YES, upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# and Mac users are advised to set this option to NO.
# The default value is: system dependent.
CASE_SENSE_NAMES = NO
# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
# their full class and namespace scopes in the documentation. If set to YES, the
# scope will be hidden.
# The default value is: NO.
HIDE_SCOPE_NAMES = YES
# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
# append additional text to a page's title, such as Class Reference. If set to
# YES the compound reference will be hidden.
# The default value is: NO.
HIDE_COMPOUND_REFERENCE= NO
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
# the files that are included by a file in the documentation of that file.
# The default value is: YES.
SHOW_INCLUDE_FILES = YES
# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
# grouped member an include statement to the documentation, telling the reader
# which file to include in order to use the member.
# The default value is: NO.
SHOW_GROUPED_MEMB_INC = NO
# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
# files with double quotes in the documentation rather than with sharp brackets.
# The default value is: NO.
FORCE_LOCAL_INCLUDES = NO
# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
# documentation for inline members.
# The default value is: YES.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
# (detailed) documentation of file and class members alphabetically by member
# name. If set to NO, the members will appear in declaration order.
# The default value is: YES.
SORT_MEMBER_DOCS = YES
# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
# descriptions of file, namespace and class members alphabetically by member
# name. If set to NO, the members will appear in declaration order. Note that
# this will also influence the order of the classes in the class list.
# The default value is: NO.
SORT_BRIEF_DOCS = NO
# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
# (brief and detailed) documentation of class members so that constructors and
# destructors are listed first. If set to NO the constructors will appear in the
# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
# member documentation.
# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
# detailed member documentation.
# The default value is: NO.
SORT_MEMBERS_CTORS_1ST = NO
# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
# of group names into alphabetical order. If set to NO the group names will
# appear in their defined order.
# The default value is: NO.
SORT_GROUP_NAMES = NO
# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
# fully-qualified names, including namespaces. If set to NO, the class list will
# be sorted only by class name, not including the namespace part.
# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
# Note: This option applies only to the class list, not to the alphabetical
# list.
# The default value is: NO.
SORT_BY_SCOPE_NAME = NO
# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
# type resolution of all parameters of a function it will reject a match between
# the prototype and the implementation of a member function even if there is
# only one candidate or it is obvious which candidate to choose by doing a
# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
# accept a match between prototype and implementation in such cases.
# The default value is: NO.
STRICT_PROTO_MATCHING = NO
# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
# list. This list is created by putting \todo commands in the documentation.
# The default value is: YES.
GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
# list. This list is created by putting \test commands in the documentation.
# The default value is: YES.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
# list. This list is created by putting \bug commands in the documentation.
# The default value is: YES.
GENERATE_BUGLIST = YES
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
# the deprecated list. This list is created by putting \deprecated commands in
# the documentation.
# The default value is: YES.
GENERATE_DEPRECATEDLIST= YES
# The ENABLED_SECTIONS tag can be used to enable conditional documentation
# sections, marked by \if ... \endif and \cond
# ... \endcond blocks.
ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
# initial value of a variable or macro / define can have for it to appear in the
# documentation. If the initializer consists of more lines than specified here
# it will be hidden. Use a value of 0 to hide initializers completely. The
# appearance of the value of individual variables and macros / defines can be
# controlled using \showinitializer or \hideinitializer command in the
# documentation regardless of this setting.
# Minimum value: 0, maximum value: 10000, default value: 30.
MAX_INITIALIZER_LINES = 30
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
# the bottom of the documentation of classes and structs. If set to YES, the
# list will mention the files that were used to generate the documentation.
# The default value is: YES.
SHOW_USED_FILES = YES
# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
# will remove the Files entry from the Quick Index and from the Folder Tree View
# (if specified).
# The default value is: YES.
SHOW_FILES = YES
# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
# page. This will remove the Namespaces entry from the Quick Index and from the
# Folder Tree View (if specified).
# The default value is: YES.
SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
# the version control system). Doxygen will invoke the program by executing (via
# popen()) the command command input-file, where command is the value of the
# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
# by doxygen. Whatever the program writes to standard output is used as the file
# version. For an example see the documentation.
FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
# by doxygen. The layout file controls the global structure of the generated
# output files in an output format independent way. To create the layout file
# that represents doxygen's defaults, run doxygen with the -l option. You can
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
# will be used as the name of the layout file.
#
# Note that if you run doxygen from a directory containing a file called
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
# tag is left empty.
LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
# extension is automatically appended if omitted. This requires the bibtex tool
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated to
# standard output by doxygen. If QUIET is set to YES this implies that the
# messages are off.
# The default value is: NO.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
# this implies that the warnings are on.
#
# Tip: Turn warnings on while writing the documentation.
# The default value is: YES.
WARNINGS = YES
# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: YES.
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some parameters
# in a documented function, or documenting parameters that don't exist or using
# markup commands wrongly.
# The default value is: YES.
WARN_IF_DOC_ERROR = YES
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
# value. If set to NO, doxygen will only warn about wrong or incomplete
# parameter documentation, but not about the absence of documentation.
# The default value is: NO.
WARN_NO_PARAMDOC = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered.
# The default value is: NO.
WARN_AS_ERROR = NO
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which
# will be replaced by the file and line number from which the warning originated
# and the warning text. Optionally the format may contain $version, which will
# be replaced by the version of the file (if it could be obtained via
# FILE_VERSION_FILTER)
# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
# The WARN_LOGFILE tag can be used to specify a file to which warning and error
# messages should be written. If left blank the output is written to standard
# error (stderr).
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag is used to specify the files and/or directories that contain
# documented source files. You may enter file names like myfile.cpp or
# directories like /usr/src/myproject. Separate the files or directories with
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = ./src \
./inc \
README.md \
COPYING.md
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see: http://www.gnu.org/software/libiconv) for the list of
# possible encodings.
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
FILE_PATTERNS = *.c \
*.cc \
*.cxx \
*.cpp \
*.c++ \
*.java \
*.ii \
*.ixx \
*.ipp \
*.i++ \
*.inl \
*.idl \
*.ddl \
*.odl \
*.h \
*.hh \
*.hxx \
*.hpp \
*.h++ \
*.cs \
*.d \
*.php \
*.php4 \
*.php5 \
*.phtml \
*.inc \
*.m \
*.markdown \
*.md \
*.mm \
*.dox \
*.py \
*.pyw \
*.f90 \
*.f95 \
*.f03 \
*.f08 \
*.f \
*.for \
*.tcl \
*.vhd \
*.vhdl \
*.ucf \
*.qsf
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
# The default value is: NO.
RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
#
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
# The default value is: NO.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS =
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# AClass::ANamespace, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS =
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
# command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank all
# files are included.
EXAMPLE_PATTERNS = *
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude commands
# irrespective of the value of the RECURSIVE tag.
# The default value is: NO.
EXAMPLE_RECURSIVE = NO
# The IMAGE_PATH tag can be used to specify one or more files or directories
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command:
#
#
#
# where is the value of the INPUT_FILTER tag, and is the
# name of an input file. Doxygen will then use the output that the filter
# program writes to standard output. If FILTER_PATTERNS is specified, this tag
# will be ignored.
#
# Note that the filter must not add or remove lines; it is applied before the
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the
# filter if there is a match. The filters are a list of the form: pattern=filter
# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
# patterns match the file name, INPUT_FILTER is applied.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will also be used to filter the input files that are used for
# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
# The default value is: NO.
FILTER_SOURCE_FILES = NO
# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
# it is also possible to disable source filtering for a specific pattern using
# *.ext= (so without naming a filter).
# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
FILTER_SOURCE_PATTERNS =
# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
# is part of the input, its contents will be placed on the main page
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE = README.md
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
# generated. Documented entities will be cross-referenced with these sources.
#
# Note: To get rid of all source code in the generated output, make sure that
# also VERBATIM_HEADERS is set to NO.
# The default value is: NO.
SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
# classes and enums directly into the documentation.
# The default value is: NO.
INLINE_SOURCES = NO
# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
# special comment blocks from generated source code fragments. Normal C, C++ and
# Fortran comments will always remain visible.
# The default value is: YES.
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
# function all documented functions referencing it will be listed.
# The default value is: NO.
REFERENCED_BY_RELATION = NO
# If the REFERENCES_RELATION tag is set to YES then for each documented function
# all documented entities called/used by that function will be listed.
# The default value is: NO.
REFERENCES_RELATION = NO
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
# to YES then the hyperlinks from functions in REFERENCES_RELATION and
# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
# link to the documentation.
# The default value is: YES.
REFERENCES_LINK_SOURCE = YES
# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
# source code will show a tooltip with additional information such as prototype,
# brief description and links to the definition and documentation. Since this
# will make the HTML file larger and loading of large files a bit slower, you
# can opt to disable this feature.
# The default value is: YES.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
SOURCE_TOOLTIPS = YES
# If the USE_HTAGS tag is set to YES then the references to source code will
# point to the HTML generated by the htags(1) tool instead of doxygen built-in
# source browser. The htags tool is part of GNU's global source tagging system
# (see http://www.gnu.org/software/global/global.html). You will need version
# 4.8.6 or higher.
#
# To use it do the following:
# - Install the latest version of global
# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
# - Make sure the INPUT points to the root of the source tree
# - Run doxygen as normal
#
# Doxygen will invoke htags (and that will in turn invoke gtags), so these
# tools must be available from the command line (i.e. in the search path).
#
# The result: instead of the source browser generated by doxygen, the links to
# source code will now point to the output of htags.
# The default value is: NO.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
USE_HTAGS = NO
# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
# verbatim copy of the header file for each class for which an include is
# specified. Set to NO to disable this.
# See also: Section \class.
# The default value is: YES.
VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
# cost of reduced performance. This can be particularly helpful with template
# rich C++ code for which doxygen's built-in parser lacks the necessary type
# information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse-libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
# compounds will be generated. Enable this if the project contains a lot of
# classes, structs, unions or interfaces.
# The default value is: YES.
ALPHABETICAL_INDEX = YES
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
# which the alphabetical index list will be split.
# Minimum value: 1, maximum value: 20, default value: 5.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
# while generating the index headers.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
# The default value is: YES.
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = html
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).
# The default value is: .html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
# each generated HTML page. If the tag is left blank doxygen will generate a
# standard header.
#
# To get valid HTML the header file that includes any scripts and style sheets
# that doxygen needs, which is dependent on the configuration options used (e.g.
# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
# default header using
# doxygen -w html new_header.html new_footer.html new_stylesheet.css
# YourConfigFile
# and then modify the file new_header.html. See also section "Doxygen usage"
# for information on how to generate the default header that doxygen normally
# uses.
# Note: The header is subject to change so you typically have to regenerate the
# default header when upgrading to a newer version of doxygen. For a description
# of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
# generated HTML page. If the tag is left blank doxygen will generate a standard
# footer. See HTML_HEADER for more information on how to generate a default
# footer and what special commands can be used inside the footer. See also
# section "Doxygen usage" for information on how to generate the default footer
# that doxygen normally uses.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
# sheet that is used by each HTML page. It can be used to fine-tune the look of
# the HTML output. If left blank doxygen will generate a default style sheet.
# See also section "Doxygen usage" for information on how to generate the style
# sheet that doxygen normally uses.
# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
# it is more robust and this tag (HTML_STYLESHEET) will in the future become
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET =
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
# created by doxygen. Using this option one can overrule certain style aspects.
# This is preferred over using HTML_STYLESHEET since it does not replace the
# standard style sheet and is therefore more robust against future updates.
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list). For an example see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET =
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
# that these files will be copied to the base HTML output directory. Use the
# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
# files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES =
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a colorwheel, see
# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
# Minimum value: 0, maximum value: 359, default value: 220.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_HUE = 220
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
# in the HTML output. For a value of 0 the output will use grayscales only. A
# value of 255 will produce the most vivid colors.
# Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_SAT = 100
# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
# luminance component of the colors in the HTML output. Values below 100
# gradually make the output lighter, whereas values above 100 make the output
# darker. The value divided by 100 is the actual gamma applied, so 80 represents
# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
# change the gamma.
# Minimum value: 40, maximum value: 240, default value: 80.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_SECTIONS = NO
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to
# such a level that at most the specified number of entries are visible (unless
# a fully collapsed tree already exceeds this amount). So setting the number of
# entries 1 will produce a full collapsed tree by default. 0 is a special value
# representing an infinite number of entries and will result in a full expanded
# tree by default.
# Minimum value: 0, maximum value: 9999, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
# environment (see: http://developer.apple.com/tools/xcode/), introduced with
# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
# Makefile in the HTML output directory. Running make will produce the docset in
# that directory and running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
# for more information.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_DOCSET = NO
# This tag determines the name of the docset feed. A documentation feed provides
# an umbrella under which multiple documentation sets from a single provider
# (such as a company or product suite) can be grouped.
# The default value is: Doxygen generated docs.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDNAME = "Doxygen generated docs"
# This tag specifies a string that should uniquely identify the documentation
# set bundle. This should be a reverse domain-name style string, e.g.
# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_BUNDLE_ID = org.doxygen.Project
# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
# the documentation publisher. This should be a reverse domain-name style
# string, e.g. com.mycompany.MyDocSet.documentation.
# The default value is: org.doxygen.Publisher.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
# The default value is: Publisher.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
# Windows.
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
# files are now used as the Windows 98 help format, and will replace the old
# Windows help format (.hlp) on all Windows platforms in the future. Compressed
# HTML files also contain an index, a table of contents, and you can search for
# words in the documentation. The HTML workshop also contains a viewer for
# compressed HTML files.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_HTMLHELP = NO
# The CHM_FILE tag can be used to specify the file name of the resulting .chm
# file. You can add a path in front of the file if the result should not be
# written to the html output directory.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
# The HHC_LOCATION tag can be used to specify the location (absolute path
# including file name) of the HTML help compiler (hhc.exe). If non-empty,
# doxygen will try to run the HTML help compiler on the generated index.hhp.
# The file has to be specified with full path.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
# (YES) or that it should be included in the master .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
GENERATE_CHI = NO
# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
# and project file content.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_INDEX_ENCODING =
# The BINARY_TOC flag controls whether a binary table of contents is generated
# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
# enables the Previous and Next buttons.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members to
# the table of contents of the HTML help documentation and to the tree view.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
TOC_EXPAND = NO
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
# (.qch) of the generated HTML documentation.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_QHP = NO
# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
# the file name of the resulting .qch file. The path specified is relative to
# the HTML output folder.
# This tag requires that the tag GENERATE_QHP is set to YES.
QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
# folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
# The QHG_LOCATION tag can be used to specify the location of Qt's
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
# generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
# generated, together with the HTML files, they form an Eclipse help plugin. To
# install this plugin and make it available under the help contents menu in
# Eclipse, the contents of the directory containing the HTML and XML files needs
# to be copied into the plugins directory of eclipse. The name of the directory
# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
# After copying Eclipse needs to be restarted before the help appears.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_ECLIPSEHELP = NO
# A unique identifier for the Eclipse help plugin. When installing the plugin
# the directory name containing the HTML and XML files should also have this
# name. Each documentation set should have its own identifier.
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
ECLIPSE_DOC_ID = org.doxygen.Project
# If you want full control over the layout of the generated HTML pages it might
# be necessary to disable the index and replace it with your own. The
# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
# of each HTML page. A value of NO enables the index and the value YES disables
# it. Since the tabs in the index contain the same information as the navigation
# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
DISABLE_INDEX = NO
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
# structure should be generated to display hierarchical information. If the tag
# value is set to YES, a side panel will be generated containing a tree-like
# index structure (just like the one that is generated for HTML Help). For this
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
# (i.e. any modern browser). Windows users are probably better off using the
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
# further fine-tune the look of the index. As an example, the default style
# sheet generated by doxygen has an example that shows how to put an image at
# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
# the same information as the tab index, you could consider setting
# DISABLE_INDEX to YES when enabling this option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = NO
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
# doxygen will group on one line in the generated HTML documentation.
#
# Note that a value of 0 will completely suppress the enum values from appearing
# in the overview section.
# Minimum value: 0, maximum value: 20, default value: 4.
# This tag requires that the tag GENERATE_HTML is set to YES.
ENUM_VALUES_PER_LINE = 4
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
# to set the initial width (in pixels) of the frame in which the tree is shown.
# Minimum value: 0, maximum value: 1500, default value: 250.
# This tag requires that the tag GENERATE_HTML is set to YES.
TREEVIEW_WIDTH = 250
# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
# external symbols imported via tag files in a separate window.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
EXT_LINKS_IN_WINDOW = NO
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
# output directory to force them to be regenerated.
# Minimum value: 8, maximum value: 50, default value: 10.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
# Note that when changing this option you need to delete any form_*.png files in
# the HTML output directory before the changes have effect.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_TRANSPARENT = YES
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# http://www.mathjax.org) which uses client side Javascript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
# to it using the MATHJAX_RELPATH option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
USE_MATHJAX = NO
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. See the MathJax site (see:
# http://docs.mathjax.org/en/latest/output.html) for more details.
# Possible values are: HTML-CSS (which is slower, but has the best
# compatibility), NativeMML (i.e. MathML) and SVG.
# The default value is: HTML-CSS.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_FORMAT = HTML-CSS
# When MathJax is enabled you need to specify the location relative to the HTML
# output directory using the MATHJAX_RELPATH option. The destination directory
# should contain the MathJax.js script. For instance, if the mathjax directory
# is located at the same level as the HTML output directory, then
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from http://www.mathjax.org before deployment.
# The default value is: http://cdn.mathjax.org/mathjax/latest.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_CODEFILE =
# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
# the HTML output. The underlying search engine uses javascript and DHTML and
# should work on any modern browser. Note that when using HTML help
# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
# there is already a search function so this one should typically be disabled.
# For large projects the javascript based search engine can be slow, then
# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
# search using the keyboard; to jump to the search box use + S
# (what the is depends on the OS and browser, but it is typically
# , /, or both). Inside the search box use the to jump into the search results window, the results can be navigated
# using the . Press to select an item or to cancel
# the search. The filter options can be selected when the cursor is inside the
# search box by pressing +. Also here use the
# to select a filter and or to activate or cancel the filter
# option.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
# implemented using a web server instead of a web client using Javascript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
# and searching needs to be provided by external tools. See the section
# "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
SERVER_BASED_SEARCH = NO
# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
# script for searching. Instead the search results are written to an XML file
# which needs to be processed by an external indexer. Doxygen will invoke an
# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
# search results.
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: http://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH = NO
# The SEARCHENGINE_URL should point to a search engine hosted by a web server
# which will return the search results when EXTERNAL_SEARCH is enabled.
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: http://xapian.org/). See the section "External Indexing and
# Searching" for details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
# search data is written to a file for indexing by an external tool. With the
# SEARCHDATA_FILE tag the name of this file can be specified.
# The default file is: searchdata.xml.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHDATA_FILE = searchdata.xml
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
# projects and redirect the results back to the right project.
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH_ID =
# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
# projects other than the one defined by this configuration file, but that are
# all added to the same external search index. Each project needs to have a
# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
# to a relative location where the documentation can be found. The format is:
# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
# The default value is: YES.
GENERATE_LATEX = YES
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: latex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked.
#
# Note that when enabling USE_PDFLATEX this option is only used for generating
# bitmaps for formulas in the HTML output, but not in the Makefile that is
# written to the output directory.
# The default file is: latex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
# index for LaTeX.
# The default file is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used by the
# printer.
# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
# 14 inches) and executive (7.25 x 10.5 inches).
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. The package can be specified just
# by its name or with the correct syntax as to be used with the LaTeX
# \usepackage command. To get the times font for instance you can specify :
# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
# To use the option intlimits with the amsmath package you can specify:
# EXTRA_PACKAGES=[intlimits]{amsmath}
# If left blank no extra packages will be included.
# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
# generated LaTeX document. The header should contain everything until the first
# chapter. If it is left blank doxygen will generate a standard header. See
# section "Doxygen usage" for information on how to let doxygen write the
# default header to a separate file.
#
# Note: Only use a user-defined header if you know what you are doing! The
# following commands have a special meaning inside the header: $title,
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
# string, for the replacement values of the other commands the user is referred
# to HTML_HEADER.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
# generated LaTeX document. The footer should contain everything after the last
# chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer.
#
# Note: Only use a user-defined footer if you know what you are doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# LaTeX style sheets that are included after the standard style sheets created
# by doxygen. Using this option one can overrule certain style aspects. Doxygen
# will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_STYLESHEET =
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the LATEX_OUTPUT output
# directory. Note that the files will be copied as-is; there are no commands or
# markers available.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_FILES =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
# contain links (just like the HTML output) instead of page references. This
# makes the output suitable for online browsing using a PDF viewer.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = YES
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# the PDF file directly from the LaTeX files. Set this option to YES, to get a
# higher quality PDF documentation.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
# if errors occur, instead of asking the user for help. This option is also used
# when generating formulas in HTML.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BATCHMODE = NO
# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
# index chapters (such as File Index, Compound Index, etc.) in the output.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HIDE_INDICES = NO
# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
# code with syntax highlighting in the LaTeX output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
# RTF output is optimized for Word 97 and may not look too pretty with other RTF
# readers/editors.
# The default value is: NO.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: rtf.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
# documents. This may be useful for small projects and may help to save some
# trees in general.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
# contain hyperlink fields. The RTF file will contain links (just like the HTML
# output) instead of page references. This makes the output suitable for online
# browsing using Word or some other Word compatible readers that support those
# fields.
#
# Note: WordPad (write) and others do not support links.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's config
# file, i.e. a series of assignments. You only have to provide replacements,
# missing definitions are set to their default value.
#
# See also section "Doxygen usage" for information on how to generate the
# default style sheet that doxygen normally uses.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
# similar to doxygen's config file. A template extensions file can be generated
# using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
# with syntax highlighting in the RTF output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
# classes and files.
# The default value is: NO.
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it. A directory man3 will be created inside the directory specified by
# MAN_OUTPUT.
# The default directory is: man.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to the generated
# man pages. In case the manual section does not start with a number, the number
# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
# optional.
# The default value is: .3.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_EXTENSION = .3
# The MAN_SUBDIR tag determines the name of the directory created within
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
# MAN_EXTENSION with the initial . removed.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_SUBDIR =
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
# will generate one additional man file for each entity documented in the real
# man page(s). These additional files only source the real man page, but without
# them the man command would be unable to find the correct page.
# The default value is: NO.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# Configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
# captures the structure of the code including all documentation.
# The default value is: NO.
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: xml.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_OUTPUT = xml
# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
# listings (including syntax highlighting and cross-referencing information) to
# the XML output. Note that enabling this will significantly increase the size
# of the XML output.
# The default value is: YES.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
# that can be used to generate PDF.
# The default value is: NO.
GENERATE_DOCBOOK = NO
# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
# front of it.
# The default directory is: docbook.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_OUTPUT = docbook
# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
# program listings (including syntax highlighting and cross-referencing
# information) to the DOCBOOK output. Note that enabling this will significantly
# increase the size of the DOCBOOK output.
# The default value is: NO.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see http://autogen.sf.net) file that captures the
# structure of the code including all documentation. Note that this feature is
# still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
# file that captures the structure of the code including all documentation.
#
# Note that this feature is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_PERLMOD = NO
# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
# output from the Perl module output.
# The default value is: NO.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
# formatted so it can be parsed by a human reader. This is useful if you want to
# understand what is going on. On the other hand, if this tag is set to NO, the
# size of the Perl module output will be much smaller and Perl will parse it
# just the same.
# The default value is: YES.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_PRETTY = YES
# The names of the make variables in the generated doxyrules.make file are
# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
# so different doxyrules.make files included by the same Makefile don't
# overwrite each other's variables.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
# C-preprocessor directives found in the sources and include files.
# The default value is: YES.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
# in the source code. If set to NO, only conditional compilation will be
# performed. Macro expansion can be done in a controlled way by setting
# EXPAND_ONLY_PREDEF to YES.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = NO
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
# EXPAND_AS_DEFINED tags.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by the
# preprocessor.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will be
# used.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that are
# defined before the preprocessor is started (similar to the -D option of e.g.
# gcc). The argument of the tag is a list of macros of the form: name or
# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
# is assumed. To prevent a macro definition from being undefined via #undef or
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
# macro definition that is found in the sources will be used. Use the PREDEFINED
# tag if you want to use a different macro definition that overrules the
# definition found in the source code.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
# an all uppercase name, and do not end with a semicolon. Such function macros
# are typically used for boiler-plate code, and will confuse the parser if not
# removed.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
#---------------------------------------------------------------------------
# The TAGFILES tag can be used to specify one or more tag files. For each tag
# file the location of the external documentation should be added. The format of
# a tag file without this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
# TAGFILES = file1=loc1 "file2 = loc2" ...
# where loc1 and loc2 can be relative or absolute paths or URLs. See the
# section "Linking to external documentation" for more information about the use
# of tag files.
# Note: Each tag file must have a unique name (where the name does NOT include
# the path). If a tag file is not located in the directory in which doxygen is
# run, you must also specify the path to the tagfile here.
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
# tag file that is based on the input files it reads. See section "Linking to
# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# the class index. If set to NO, only the inherited external classes will be
# listed.
# The default value is: NO.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will be
# listed.
# The default value is: YES.
EXTERNAL_GROUPS = YES
# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
# the related pages index. If set to NO, only the current project's pages will
# be listed.
# The default value is: YES.
EXTERNAL_PAGES = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of 'which perl').
# The default file (with absolute path) is: /usr/bin/perl.
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
# NO turns the diagrams off. Note that this option also works with HAVE_DOT
# disabled, but it is recommended to install and use dot, since it yields more
# powerful graphs.
# The default value is: YES.
CLASS_DIAGRAMS = YES
# You can define message sequence charts within doxygen comments using the \msc
# command. Doxygen will then run the mscgen tool (see:
# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
# documentation. The MSCGEN_PATH tag allows you to specify the directory where
# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
MSCGEN_PATH =
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
DIA_PATH =
# If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class.
# The default value is: YES.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz (see:
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: NO.
HAVE_DOT = NO
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
# processors available in the system. You can set it explicitly to a value
# larger than 0 to get control over the balance between CPU load and processing
# speed.
# Minimum value: 0, maximum value: 32, default value: 0.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NUM_THREADS = 0
# When you want a differently looking font in the dot files that doxygen
# generates you can specify the font name using DOT_FONTNAME. You need to make
# sure dot is able to find the font, which can be done by putting it in a
# standard location or by setting the DOTFONTPATH environment variable or by
# setting DOT_FONTPATH to the directory containing the font.
# The default value is: Helvetica.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTNAME = Helvetica
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
# dot graphs.
# Minimum value: 4, maximum value: 24, default value: 10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTSIZE = 10
# By default doxygen will tell dot to use the default font as specified with
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
# the path where dot can find it using this tag.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
# each documented class showing the direct and indirect inheritance relations.
# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation
# dependencies (inheritance, containment, and class references variables) of the
# class with other documented classes.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GROUP_GRAPHS = YES
# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
UML_LOOK = NO
# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
# class node. If there are many fields or methods and many nodes the graph may
# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
# number of items for each type to make the size more manageable. Set this to 0
# for no limit. Note that the threshold may be exceeded by 50% before the limit
# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
# but if the number exceeds 15, the total amount of fields shown is limited to
# 10.
# Minimum value: 0, maximum value: 100, default value: 10.
# This tag requires that the tag HAVE_DOT is set to YES.
UML_LIMIT_NUM_FIELDS = 10
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their
# instances.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
TEMPLATE_RELATIONS = NO
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDE_GRAPH = YES
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
# set to YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other documented
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDED_BY_GRAPH = YES
# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
# dependency graph for every global function or class method.
#
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command. Disabling a call graph can be
# accomplished by means of the command \hidecallgraph.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
CALL_GRAPH = NO
# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
# dependency graph for every global function or class method.
#
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable caller graphs for selected
# functions only using the \callergraph command. Disabling a caller graph can be
# accomplished by means of the command \hidecallergraph.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
CALLER_GRAPH = NO
# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
# hierarchy of all classes instead of a textual one.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GRAPHICAL_HIERARCHY = YES
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
# dependencies a directory has on other directories in a graphical way. The
# dependency relations are determined by the #include relations between the
# files in the directories.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
DIRECTORY_GRAPH = YES
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
# http://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus.
# The default value is: png.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_IMAGE_FORMAT = png
# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
# enable generation of interactive SVG images that allow zooming and panning.
#
# Note that this requires a modern browser other than Internet Explorer. Tested
# and working are Firefox, Chrome, Safari, and Opera.
# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
# the SVG files visible. Older versions of IE do not have SVG support.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
INTERACTIVE_SVG = NO
# The DOT_PATH tag can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the \dotfile
# command).
# This tag requires that the tag HAVE_DOT is set to YES.
DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
MSCFILE_DIRS =
# The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile
# command).
DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
# path where java can find the plantuml.jar file. If left blank, it is assumed
# PlantUML is not used or called during a preprocessing step. Doxygen will
# generate a warning when it encounters a \startuml command in this case and
# will not generate output for the diagram.
PLANTUML_JAR_PATH =
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
# configuration file for plantuml.
PLANTUML_CFG_FILE =
# When using plantuml, the specified paths are searched for files specified by
# the !include statement in a plantuml block.
PLANTUML_INCLUDE_PATH =
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
# that will be shown in the graph. If the number of nodes in a graph becomes
# larger than this value, doxygen will truncate the graph, which is visualized
# by representing a node as a red box. Note that doxygen if the number of direct
# children of the root node in a graph is already larger than
# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
# Minimum value: 0, maximum value: 10000, default value: 50.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_GRAPH_MAX_NODES = 50
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
# generated by dot. A depth value of 3 means that only nodes reachable from the
# root by following a path via at most 3 edges will be shown. Nodes that lay
# further from the root node will be omitted. Note that setting this option to 1
# or 2 may greatly reduce the computation time needed for large code bases. Also
# note that the size of a graph can be further restricted by
# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
# Minimum value: 0, maximum value: 1000, default value: 0.
# This tag requires that the tag HAVE_DOT is set to YES.
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
# background. This is disabled by default, because dot on Windows does not seem
# to support this out of the box.
#
# Warning: Depending on the platform used, enabling this option may lead to
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
# read).
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_TRANSPARENT = NO
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
# this, this feature is disabled by default.
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_MULTI_TARGETS = NO
# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
# explaining the meaning of the various boxes and arrows in the dot generated
# graphs.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
# files that are used to generate the various graphs.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_CLEANUP = YES
================================================
FILE: README.md
================================================
# TR2Main
This is an open source implementation of the classic Tomb Raider II game (1997), made by reverse engineering of the original game for PC and PS1. Right now it's a dynamic library injecting the original game EXE to allow step-by-step decompilation, fixing, and improvement, but someday it will become single EXE file.
The goal of the project is to make 1:1 clone of the original PC game, to fix all its bugs, and to add more features from non-PC official ports (for example from PS1). So TR2Main is some sort of unofficial modern PC port of Tomb Raider II.
## Getting Started
You may download patched Tomb2.exe, TR2Main.dll from [releases](https://github.com/Arsunt/TR2Main/releases), put them in your game folder and just launch. Also you may build TR2Main.dll yourself and use it with patched Tomb2.exe from [here](/binaries).
The Golden Mask game is included into a [release pack](https://github.com/Arsunt/TR2Main/releases), since it's freeware. The original game level files must be officially obtained via Steam/GOG/CD/whatever.
## Changelog
Learn about [the latest improvements](CHANGELOG.md).
## Built With
* [Code::Blocks](http://www.codeblocks.org) - C/C++ IDE used
* [GCC](https://gcc.gnu.org) - C/C++ compiler
* [Doxygen](http://www.doxygen.org) - documentation
## Authors
* **Michael Chaban** \([Arsunt](https://github.com/Arsunt)\). Author of the project. E-mail:
* [**ChocolateFan**](https://github.com/asasas9500). Decompilation.
* [**TokyoSU**](https://github.com/TokyoSU). Decompilation and features development.
* **PaulD**. Author of [CD audio solution](modding/cd_pauld.cpp) used now by Steam.
* [**Lito Perezito**](https://litoperezito.com). The artist who created loading screens for The Golden Mask.
## License
This project is licensed under the GNU General Public License - see the [COPYING.md](COPYING.md) file for details
## Acknowledgments
If you are inspired by my project and decided to borrow some of the ideas found in the code, do not forget provide a link to this project. It took me years to decompile and understand the details required for this project.
## Copyright
(c) 2017-2023 Michael Chaban. All rights reserved.
Original game is created by Core Design Ltd. in 1997.
Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
================================================
FILE: TR2Main.cbp
================================================
================================================
FILE: TR2Main.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
/**
* @file
* @brief TR2Main interface
*
* This file implements TR2Main.DLL exported functions and interfaces
*/
/**
* @defgroup TR2MAIN TR2Main
* @brief TR2Main interface
*
* This module contains TR2Main.DLL exported functions and interfaces
*
* @{
*/
#include
/** @cond Doxygen_Suppress */
#ifdef BUILD_DLL
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
/** @endcond */
HINSTANCE hInstance = NULL;
// 3d system
extern void Inject_3Dgen();
extern void Inject_3Dout();
extern void Inject_3Dinsert();
extern void Inject_PhdMath();
extern void Inject_ScaleSpr();
// game
extern void Inject_Bird();
extern void Inject_Boat();
extern void Inject_Box();
extern void Inject_Camera();
extern void Inject_Cinema();
extern void Inject_Collide();
extern void Inject_Control();
extern void Inject_Demo();
extern void Inject_Diver();
extern void Inject_Dog();
extern void Inject_Dragon();
extern void Inject_Draw();
extern void Inject_Eel();
extern void Inject_Effects();
extern void Inject_Enemies();
extern void Inject_Gameflow();
extern void Inject_Hair();
extern void Inject_Health();
extern void Inject_Inventory();
extern void Inject_InvFunc();
extern void Inject_InvText();
extern void Inject_Items();
extern void Inject_Lara();
extern void Inject_Lara1Gun();
extern void Inject_Lara2Gun();
extern void Inject_LaraClimb();
extern void Inject_LaraFire();
extern void Inject_LaraFlare();
extern void Inject_LaraMisc();
extern void Inject_LaraSurf();
extern void Inject_LaraSwim();
extern void Inject_Lot();
extern void Inject_Missile();
extern void Inject_MoveBlock();
extern void Inject_Objects();
extern void Inject_People();
extern void Inject_Pickup();
extern void Inject_Rat();
extern void Inject_SaveGame();
extern void Inject_Setup();
extern void Inject_Shark();
extern void Inject_Skidoo();
extern void Inject_Sound();
extern void Inject_Sphere();
extern void Inject_Spider();
extern void Inject_Text();
extern void Inject_Traps();
extern void Inject_Yeti();
// specific
extern void Inject_Background();
extern void Inject_Display();
extern void Inject_File();
extern void Inject_Fmv();
extern void Inject_Frontend();
extern void Inject_Game();
extern void Inject_HWR();
extern void Inject_Init();
extern void Inject_Init3d();
extern void Inject_InitDisplay();
extern void Inject_InitInput();
extern void Inject_InitSound();
extern void Inject_Input();
extern void Inject_Option();
extern void Inject_Output();
extern void Inject_Registry();
extern void Inject_Screenshot();
extern void Inject_SetupDlg();
extern void Inject_SetupWnd();
extern void Inject_SMain();
extern void Inject_SndPC();
extern void Inject_Texture();
extern void Inject_Utils();
extern void Inject_WinMain();
extern void Inject_WinVid();
static void Inject() {
// 3d system
Inject_3Dgen();
Inject_3Dout();
Inject_3Dinsert();
Inject_PhdMath();
Inject_ScaleSpr();
// game
Inject_Bird();
Inject_Boat();
Inject_Box();
Inject_Camera();
Inject_Cinema();
Inject_Collide();
Inject_Control();
Inject_Demo();
Inject_Diver();
Inject_Dog();
Inject_Dragon();
Inject_Draw();
Inject_Eel();
Inject_Effects();
Inject_Enemies();
Inject_Gameflow();
Inject_Hair();
Inject_Health();
Inject_Inventory();
Inject_InvFunc();
Inject_InvText();
Inject_Items();
Inject_Lara();
Inject_Lara1Gun();
Inject_Lara2Gun();
Inject_LaraClimb();
Inject_LaraFire();
Inject_LaraFlare();
Inject_LaraMisc();
Inject_LaraSurf();
Inject_LaraSwim();
Inject_Lot();
Inject_Missile();
Inject_MoveBlock();
Inject_Objects();
Inject_People();
Inject_Pickup();
Inject_Rat();
Inject_SaveGame();
Inject_Setup();
Inject_Shark();
Inject_Skidoo();
Inject_Sound();
Inject_Sphere();
Inject_Spider();
Inject_Text();
Inject_Traps();
Inject_Yeti();
// specific
Inject_Background();
Inject_Display();
Inject_File();
Inject_Fmv();
Inject_Frontend();
Inject_Game();
Inject_HWR();
Inject_Init();
Inject_Init3d();
Inject_InitDisplay();
Inject_InitInput();
Inject_InitSound();
Inject_Input();
Inject_Option();
Inject_Output();
Inject_Registry();
Inject_Screenshot();
Inject_SetupDlg();
Inject_SetupWnd();
Inject_SMain();
Inject_SndPC();
Inject_Texture();
Inject_Utils();
Inject_WinMain();
Inject_WinVid();
}
extern "C" DLL_EXPORT int DummyFunction() {
return 0;
}
/**
* An optional entry point into a dynamic-link library (DLL)
* @param[in] hinstDLL A handle to the DLL module
* @param[in] fdwReason The reason code that indicates why the DLL
* entry-point function is being called
* @param[in] lpvReserved used if fdwReason is DLL_PROCESS_ATTACH or DLL_PROCESS_DETACH
* @return TRUE if it succeeds or FALSE if it fails
* @note See the MSDN for more information
*/
extern "C" BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch( fdwReason ) {
case DLL_PROCESS_ATTACH :
// attach to process
hInstance = hinstDLL;
Inject();
break;
case DLL_PROCESS_DETACH :
// detach from process
break;
case DLL_THREAD_ATTACH :
// attach to thread
break;
case DLL_THREAD_DETACH :
// detach from thread
break;
}
return TRUE; // successful
}
/** @} */
================================================
FILE: TR2Main.rc
================================================
/*
* Copyright (c) 2017-2023 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include
#define VER_MAJOR 0
#define VER_MINOR 9
#define VER_PATCH 0
#define VER_BUILD 0
#define STR_(x) #x
#define STR(x) STR_(x)
#define VER_FULL STR(VER_MAJOR) "." STR(VER_MINOR) "." STR(VER_PATCH) "." STR(VER_BUILD) "\0"
#define VER_SHORT STR(VER_MAJOR) "." STR(VER_MINOR) "\0"
// DLL version information.
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_MAJOR,VER_MINOR,VER_PATCH,VER_BUILD
PRODUCTVERSION VER_MAJOR,VER_MINOR,VER_PATCH,VER_BUILD
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG | VS_FF_PRERELEASE
#else
FILEFLAGS 0
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080904b0"
BEGIN
VALUE "Comments", "https://github.com/Arsunt/TR2Main"
VALUE "CompanyName", "Michael Chaban (arsunt@gmail.com)"
VALUE "FileDescription", "Tomb Raider II Main Library"
VALUE "FileVersion", VER_FULL
VALUE "InternalName", "TR2Main"
VALUE "LegalCopyright", " 2017-2023 Michael Chaban"
VALUE "LegalTrademarks", "Original game is created by Core Design Ltd. in 1997. Lara Croft and Tomb Raider are trademarks of Embracer Group AB."
VALUE "OriginalFilename", "TR2Main.dll"
VALUE "ProductName", "Tomb Raider II Community Edition"
VALUE "ProductVersion", VER_SHORT
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x809, 1200
END
END
// Embedded resources
BUTTONS.JSON RCDATA "embedded/BUTTONS.JSON"
BUTTONS.PCX RCDATA "embedded/BUTTONS.PCX"
================================================
FILE: TR2_progress.txt
================================================
x function is unused / included in another function
+ function is reimplemented in DLL
* function used by DLL from EXE
// ======================================
// 3D SYSTEM
// ======================================
3dsystem/3d_gen.cpp
0x00401000: + phd_GenerateW2V
0x004011D0: + phd_LookAt
0x00401250: + phd_GetVectorAngles
0x004012D0: + phd_RotX
0x00401380: + phd_RotY
0x00401430: + phd_RotZ
0x004014E0: + phd_RotYXZ
0x004016C0: + phd_RotYXZpack
0x004018B0: + phd_TranslateRel
0x00401960: + phd_TranslateAbs
0x004019E0: + phd_PutPolygons
0x00401AE0: + S_InsertRoom
0x00401BD0: + calc_background_light
0x00401C10: + S_InsertBackground
----------: + S_InsertInvBgnd
0x00401D50: + calc_object_vertices
0x00401F30: + calc_vertice_light
0x004020A0: + calc_roomvert
0x00402320: + phd_RotateLight
0x004023F0: + phd_InitPolyList
0x00402420: + phd_SortPolyList
0x00402460: + do_quickysorty
0x00402530: + phd_PrintPolyList
0x00402570: + AlterFOV
0x00402680: + phd_SetNearZ
0x004026D0: + phd_SetFarZ
0x004026F0: + phd_InitWindow
----------: + phd_PopMatrix
---asm----: + phd_PushMatrix
---asm----: + phd_PushUnitMatrix
3dsystem/3d_out.cpp
0x00402960: + draw_poly_line
0x00402B00: + draw_poly_flat
0x00402B40: + draw_poly_trans
0x00402B80: + draw_poly_gouraud
0x00402BC0: + draw_poly_gtmap
0x00402C00: + draw_poly_wgtmap
0x00402C40: + xgen_x
0x00402D20: + xgen_xg
0x00402E70: + xgen_xguv
0x00403090: + xgen_xguvpersp_fp
0x00403320: + gtmap_persp32_fp
0x004042F0: + wgtmap_persp32_fp
0x004057C0: + draw_poly_gtmap_persp
0x00405800: + draw_poly_wgtmap_persp
---asm----: + flatA
---asm----: + transA
---asm----: + gourA
---asm----: + gtmapA
---asm----: + wgtmapA
3dsystem/3dinsert.cpp
0x00405840: + visible_zclip
0x004058B0: + ZedClipper
0x004059F0: + XYGUVClipper
0x00405F10: + InsertObjectGT4
0x00406970: + InsertObjectGT3
0x004071F0: + XYGClipper
0x00407620: + InsertObjectG4
0x00407A00: + InsertObjectG3
0x00407D20: + XYClipper
0x00407FF0: + InsertTrans8
0x004084A0: + InsertTransQuad
0x00408580: + InsertFlatRect
0x00408650: + InsertLine
0x00408710: + InsertGT3_ZBuffered
0x00408D60: + DrawClippedPoly_Textured
0x00408EA0: + InsertGT4_ZBuffered
0x004092E0: + InsertObjectGT4_ZBuffered
0x00409380: + InsertObjectGT3_ZBuffered
0x00409430: + InsertObjectG4_ZBuffered
0x004097D0: + DrawPoly_Gouraud
0x004098D0: + InsertObjectG3_ZBuffered
0x00409BB0: + InsertFlatRect_ZBuffered
0x00409D80: + InsertLine_ZBuffered
0x00409EC0: + InsertGT3_Sorted
0x0040A5D0: + InsertClippedPoly_Textured
0x0040A780: + InsertGT4_Sorted
0x0040AC60: + InsertObjectGT4_Sorted
0x0040ACF0: + InsertObjectGT3_Sorted
0x0040AD90: + InsertObjectG4_Sorted
0x0040B1D0: + InsertPoly_Gouraud
0x0040B350: + InsertObjectG3_Sorted
0x0040B6A0: + InsertSprite_Sorted
0x0040B9F0: + InsertFlatRect_Sorted
0x0040BB70: + InsertLine_Sorted
0x0040BCA0: + InsertTrans8_Sorted
0x0040BE40: + InsertTransQuad_Sorted
0x0040BF80: + InsertSprite
3dsystem/phd_math.cpp
---asm----: + phd_atan
---asm----: + phd_cos
---asm----: + phd_sin
---asm----: + phd_sqrt
3dsystem/scalespr.cpp
0x0040C030: + S_DrawSprite
0x0040C300: + S_DrawPickup
0x0040C390: + ins_room_sprite
0x0040C4F0: + S_DrawScreenSprite2d
0x0040C590: + S_DrawScreenSprite
0x0040C630: + draw_scaled_spriteC
// ======================================
// GAME
// ======================================
game/bird.cpp
0x0040C860: * InitialiseEagle
0x0040C8F0: * EagleControl
game/boat.cpp
0x0040CB10: InitialiseBoat
0x0040CB50: BoatCheckGeton
0x0040CCC0: BoatCollision
0x0040CE20: TestWaterHeight
0x0040CF20: DoBoatShift
0x0040D0F0: + DoWakeEffect
0x0040D270: DoBoatDynamics
0x0040D2C0: BoatDynamics
0x0040D7A0: BoatUserControl
0x0040D930: BoatAnimation
0x0040DAA0: BoatControl
0x0040E0D0: + GondolaControl
game/box.cpp
0x0040E190: * InitialiseCreature
0x0040E1C0: CreatureActive
0x0040E210: * CreatureAIInfo
0x0040E470: SearchLOT
0x0040E670: UpdateLOT
0x0040E6E0: TargetBox
0x0040E780: StalkBox
0x0040E880: EscapeBox
0x0040E930: ValidBox
0x0040E9E0: * CreatureMood
0x0040EE50: CalculateTarget
0x0040F2B0: CreatureCreature
0x0040F3B0: BadFloor
0x0040F440: + CreatureDie
0x0040F500: * CreatureAnimation
0x0040FDD0: * CreatureTurn
0x0040FEB0: * CreatureTilt
0x0040FEF0: * CreatureHead
0x0040FF40: CreatureNeck
0x0040FF90: CreatureFloat
0x00410040: CreatureUnderwater
0x00410090: * CreatureEffect
0x004100F0: CreatureVault
0x00410230: + CreatureKill
0x004103A0: GetBaddieTarget
game/camera.cpp
0x00410580: + InitialiseCamera
0x00410630: + MoveCamera
0x004109B0: * ClipCamera
0x00410A90: * ShiftCamera
0x00410BF0: * GoodPosition
0x00410C40: * SmartShift
0x004113D0: * ChaseCamera
0x004114C0: * ShiftClamp
0x00411660: * CombatCamera
0x004117F0: + LookCamera
0x004119E0: + FixedCamera
0x00411A80: + CalculateCamera
game/cinema.cpp
0x00411F30: + SetCutsceneTrack
0x00411F40: + StartCinematic
0x00412060: + InitCinematicRooms
0x00412100: + DoCinematic
0x00412270: * CalculateCinematicCamera
0x004123B0: * GetCinematicRoom
0x00412430: * ControlCinematicPlayer
0x00412510: * LaraControlCinematic
0x004125B0: * InitialisePlayer1
0x00412640: * InitialiseGenPlayer
0x00412680: * InGameCinematicCamera
game/collide.cpp
0x004128D0: GetCollisionInfo
0x00412F90: FindGridShift
0x00412FC0: + CollideStaticObjects
0x004133B0: + GetNearByRooms
0x00413480: + GetNewRoom
0x004134E0: ShiftItem
0x00413520: * UpdateLaraRoom
0x00413580: GetTiltType
0x00413620: LaraBaddieCollision
0x004137C0: EffectSpaz
0x00413840: * CreatureCollision
0x004138C0: * ObjectCollision
0x00413920: DoorCollision
0x004139A0: TrapCollision
0x00413A10: ItemPushLara
0x00413D20: TestBoundsCollide
0x00413DF0: TestLaraPosition
0x00413F30: AlignLaraPosition
0x00414070: MoveLaraPosition
0x00414200: Move3DPosTo3DPos
game/control.cpp
0x00414370: + ControlPhase
0x004146C0: * AnimateItem
0x00414A30: GetChange
0x00414AE0: TranslateItem
0x00414B40: * GetFloor
0x00414CE0: * GetWaterHeight
0x00414E50: * GetHeight
0x004150D0: RefreshCamera
0x004151C0: * TestTriggers
0x004158A0: * TriggerActive
0x00415900: * GetCeiling
0x00415B60: GetDoor
0x00415BB0: * LOS
0x00415C50: zLOS
0x00415F40: xLOS
0x00416230: ClipTarget
0x00416310: ObjectOnLOS
0x00416610: * FlipMap
0x004166D0: RemoveRoomFlipItems
0x00416770: AddRoomFlipItems
0x004167D0: + TriggerCDTrack
0x00416800: + TriggerNormalCDTrack
game/demo.cpp
0x004168E0: * DoDemoSequence
0x00416940: + StartDemo
0x00416AF0: * LoadLaraDemoPos
0x00416BC0: * GetDemoInput
game/diver.cpp
0x00416BF0: Harpoon
0x00416C70: GetWaterSurface
0x00416D80: * DiverControl
game/dog.cpp
0x00417130: * DogControl
0x004174E0: * TigerControl
game/dragon.cpp
0x00417780: ControlTwinkle
0x00417900: CreateBartoliLight
0x004179E0: DragonFire
0x00417A90: * DragonCollision
0x00417D80: * DragonBones
0x00417E60: * DragonControl
0x004183B0: * InitialiseBartoli
0x004184D0: * BartoliControl
0x00418670: * DinoControl
game/draw.cpp
0x00418920: * DrawPhaseCinematic
0x00418960: * DrawPhaseGame
0x004189A0: + DrawRooms
0x00418C50: + GetRoomBounds
0x00418E20: + SetRoomBounds
0x004191A0: + ClipRoom
0x00419580: + PrintRooms
0x00419640: + PrintObjects
0x00419870: + DrawEffect
0x004199C0: + DrawSpriteItem
----------: + DrawDummyItem
0x00419A50: + DrawAnimatingItem
0x00419DD0: * DrawLara
0x0041AB00: + DrawLaraInt
0x0041B6F0: * InitInterpolate
0x0041B730: * phd_PopMatrix_I
0x0041B760: * phd_PushMatrix_I
0x0041B790: * phd_RotY_I
0x0041B7D0: * phd_RotX_I
0x0041B810: * phd_RotZ_I
0x0041B850: * phd_TranslateRel_I
0x0041B8A0: * phd_TranslateRel_ID
0x0041B8F0: * phd_RotYXZ_I
0x0041B940: * phd_RotYXZsuperpack_I
0x0041B980: + phd_RotYXZsuperpack
0x0041BA30: + phd_PutPolygons_I
0x0041BA60: * InterpolateMatrix
0x0041BC10: * InterpolateArmMatrix
0x0041BD10: + DrawGunFlash
0x0041BE80: * CalculateObjectLighting
0x0041BF70: * GetFrames
0x0041C010: * GetBoundsAccurate
0x0041C090: * GetBestFrame
0x0041C0D0: + AddDynamicLight
game/eel.cpp
0x0041C120: * BigEelControl
0x0041C2C0: * EelControl
game/effects.cpp
0x0041C4B0: + ItemNearLara
0x0041C540: + SoundEffects
0x0041C5B0: + DoBloodSplat
0x0041C610: + DoLotsOfBlood
0x0041C6C0: + ControlBlood1
0x0041C750: + ControlExplosion1
0x0041C7D0: + Richochet
0x0041C850: + ControlRichochet1
0x0041C880: + CreateBubble
0x0041C8F0: + LaraBubbles
0x0041C970: + ControlBubble1
0x0041CA70: + Splash
----------: + WadeSplash
0x0041CB40: + ControlSplash1
0x0041CBC0: + ControlWaterSprite
0x0041CC70: + ControlSnowSprite
0x0041CD00: + ControlHotLiquid
0x0041CDE0: + WaterFall
0x0041CF20: + finish_level_effect
0x0041CF30: + turn180_effect
0x0041CF50: + floor_shake_effect
0x0041CFF0: + lara_normal_effect
0x0041D030: + BoilerFX
0x0041D050: + FloodFX
0x0041D0E0: + RubbleFX
0x0041D110: + ChandelierFX
0x0041D140: + ExplosionFX
0x0041D170: + PistonFX
0x0041D190: + CurtainFX
0x0041D1B0: + StatueFX
0x0041D1D0: + SetChangeFX
0x0041D1F0: + ControlDingDong
0x0041D230: + ControlLaraAlarm
0x0041D270: + ControlAlarmSound
0x0041D2E0: + ControlBirdTweeter
0x0041D340: + DoChimeSound
0x0041D3A0: + ControlClockChimes
0x0041D410: + SphereOfDoomCollision
0x0041D540: + SphereOfDoom
0x0041D630: + DrawSphereOfDoom
0x0041D760: + lara_hands_free
0x0041D770: + flip_map_effect
0x0041D780: + draw_right_gun
0x0041D7D0: + draw_left_gun
----------: shoot_right_gun
----------: shoot_left_gun
0x0041D820: + swap_meshes_with_meshswap1
0x0041D890: + swap_meshes_with_meshswap2
0x0041D900: + swap_meshes_with_meshswap3
0x0041D9A0: + invisibility_on
0x0041D9B0: + invisibility_off
0x0041D9D0: + dynamic_light_on
0x0041D9E0: + dynamic_light_off
0x0041D9F0: + reset_hair
0x0041DA00: + AssaultStart
0x0041DA30: + AssaultStop
0x0041DA50: + AssaultReset
0x0041DA70: + AssaultFinished
game/enemies.cpp
0x0041DB30: Knife
0x0041DBB0: * Cult2Control
0x0041DFE0: * MonkControl
0x0041E4B0: * Worker3Control
0x0041EAC0: * DrawXianLord
0x0041EEC0: XianDamage
0x0041EF70: * InitialiseXianLord
0x0041EFD0: * XianLordControl
0x0041F5B0: WarriorSparkleTrail
0x0041F650: * WarriorControl
game/gameflow.cpp
0x0041FA40: + GF_LoadScriptFile
0x0041FC30: + GF_DoFrontEndSequence
0x0041FC50: + GF_DoLevelSequence
0x0041FCC0: + GF_InterpretSequence
0x004201A0: + GF_ModifyInventory
game/hair.cpp
0x00420E80: * InitialiseHair
0x00420F00: * HairControl
0x00421900: * DrawHair
game/health.cpp
0x00421980: + FlashIt
0x004219B0: + DrawAssaultTimer
0x00421B00: + DrawGameInfo
0x00421B50: + DrawHealthBar
0x00421C00: + DrawAirBar
0x00421CA0: + MakeAmmoString
0x00421CD0: + DrawAmmoInfo
0x00421E20: + InitialisePickUpDisplay
0x00421E40: + DrawPickups
0x00421F40: + AddDisplayPickup
0x00421FB0: + DisplayModeInfo
0x00422030: + DrawModeInfo
game/inventory.cpp
0x00422060: + Display_Inventory
0x004232F0: + Construct_Inventory
0x00423450: + SelectMeshes
0x004234E0: + AnimateInventoryItem
0x00423570: + DrawInventoryItem
0x004239A0: + GetDebouncedInput
0x004239C0: + DoInventoryPicture
0x004239D0: + DoInventoryBackground
game/invfunc.cpp
0x00423B10: + InitColours
0x00423C20: + RingIsOpen
0x00423D90: + RingIsNotOpen
0x00423E20: + RingNotActive
0x00424290: + RingActive
0x004242D0: + Inv_AddItem
0x00424AE0: + Inv_InsertItem
0x00424C10: + Inv_RequestItem
0x00424C90: + Inv_RemoveAllItems
0x00424CB0: + Inv_RemoveItem
0x00424DC0: + Inv_GetItemOption
0x00424FB0: + RemoveInventoryText
0x00424FE0: + Inv_RingInit
0x004250F0: + Inv_RingGetView
0x00425150: + Inv_RingLight
0x00425190: + Inv_RingCalcAdders
0x004251C0: + Inv_RingDoMotions
0x00425300: + Inv_RingRotateLeft
0x00425330: + Inv_RingRotateRight
0x00425360: + Inv_RingMotionInit
0x004253D0: + Inv_RingMotionSetup
0x00425400: + Inv_RingMotionRadius
0x00425430: + Inv_RingMotionRotation
0x00425460: + Inv_RingMotionCameraPos
0x00425490: + Inv_RingMotionCameraPitch
0x004254B0: + Inv_RingMotionItemSelect
0x00425510: + Inv_RingMotionItemDeselect
game/invtext.cpp
0x00425580: + Init_Requester
0x00425610: + Remove_Requester
0x004256C0: + ReqItemCentreAlign
0x004256E0: + ReqItemLeftAlign
0x00425740: + ReqItemRightAlign
0x004257A0: + Display_Requester
0x00426010: + SetRequesterHeading
0x004260C0: + RemoveAllReqItems
0x004260E0: + ChangeRequesterItem
0x004261A0: + AddRequesterItem
0x00426250: + SetPCRequesterSize
0x00426290: + AddAssaultTime
0x00426320: + ShowGymStatsText
0x00426500: + ShowStatsText
0x004268A0: + ShowEndStatsText
game/items.cpp
0x00426CD0: + InitialiseItemArray
0x00426D30: * KillItem
0x00426E50: * CreateItem
0x00426E90: + InitialiseItem
0x00427050: * RemoveActiveItem
0x004270E0: * RemoveDrawnItem
0x00427150: + AddActiveItem
0x004271B0: * ItemNewRoom
0x00427250: + GlobalItemReplace
0x004272D0: * InitialiseFXArray
0x00427300: * CreateEffect
0x00427370: * KillEffect
0x00427460: * EffectNewRoom
0x00427500: * ClearBodyBag
game/lara.cpp
0x00427560: * LaraAboveWater
0x00427700: LookUpDown
0x00427770: LookLeftRight
0x004277F0: ResetLook
0x00427880: lara_as_walk
0x00427910: lara_as_run
0x00427A60: lara_as_stop
0x00427BB0: lara_as_forwardjump
----------: lara_as_pose
0x00427C90: lara_as_fastback
0x00427CF0: lara_as_turn_r
0x00427D80: lara_as_turn_l
0x00427E10: lara_as_death
0x00427E30: lara_as_fastfall
0x00427E70: lara_as_hang
0x00427ED0: lara_as_reach
0x00427EF0: lara_as_splat
----------: lara_as_land
0x00427F00: lara_as_compress
0x00428010: lara_as_back
0x004280A0: lara_as_null
0x004280B0: lara_as_fastturn
0x00428100: lara_as_stepright
0x00428180: lara_as_stepleft
0x00428200: lara_as_slide
0x00428230: lara_as_backjump
0x00428280: lara_as_rightjump
0x004282C0: lara_as_leftjump
0x00428300: lara_as_upjump
0x00428320: lara_as_fallback
0x00428350: lara_as_hangleft
0x00428390: lara_as_hangright
0x004283D0: lara_as_slideback
0x004283F0: lara_as_pushblock
----------: lara_as_pullblock
0x00428420: lara_as_ppready
0x00428450: lara_as_pickup
0x00428480: lara_as_pickupflare
0x004284E0: lara_as_switchon
----------: lara_as_switchoff
0x00428520: lara_as_usekey
----------: lara_as_usepuzzle
----------: lara_as_roll
----------: lara_as_roll2
0x00428550: lara_as_special
----------: lara_as_usemidas
----------: lara_as_diemidas
0x00428570: lara_as_swandive
0x004285A0: lara_as_fastdive
----------: lara_as_gymnast
0x00428600: lara_as_waterout
----------: lara_as_laratest1
----------: lara_as_laratest2
----------: lara_as_laratest3
0x00428620: lara_as_wade
----------: lara_as_twist
----------: lara_as_kick
0x004286F0: lara_as_deathslide
0x00428790: extra_as_breath
----------: extra_as_plunger
0x004287E0: extra_as_yetikill
0x00428830: extra_as_sharkkill
0x004288D0: extra_as_airlock
0x004288F0: extra_as_gongbong
0x00428910: extra_as_dinokill
0x00428970: extra_as_pulldagger
0x00428A30: extra_as_startanim
0x00428A80: extra_as_starthouse
0x00428B30: extra_as_finalanim
0x00428BE0: LaraFallen
0x00428C40: LaraCollideStop
0x00428D00: lara_col_walk
0x00428EA0: lara_col_run
0x00429020: lara_col_stop
0x004290B0: lara_col_forwardjump
----------: lara_col_pose
0x00429190: lara_col_fastback
0x00429250: lara_col_turn_r
0x004292F0: lara_col_turn_l
0x00429310: lara_col_death
0x00429380: lara_col_fastfall
0x00429420: lara_col_hang
0x00429550: lara_col_reach
0x004295E0: lara_col_splat
----------: lara_col_land
0x00429640: lara_col_compress
0x004296E0: lara_col_back
----------: lara_col_null
0x004297E0: lara_col_fastturn
0x00429800: lara_col_stepright
0x004298C0: lara_col_stepleft
0x004298E0: lara_col_slide
0x00429900: lara_col_backjump
0x00429930: lara_col_rightjump
0x00429960: lara_col_leftjump
0x00429990: lara_col_upjump
0x00429AD0: lara_col_fallback
0x00429B60: lara_col_hangleft
0x00429BA0: lara_col_hangright
0x00429BE0: lara_col_slideback
----------: lara_col_pushblock
----------: lara_col_pullblock
----------: lara_col_ppready
----------: lara_col_pickup
----------: lara_col_switchon
----------: lara_col_switchoff
----------: lara_col_usekey
----------: lara_col_usepuzzle
0x00429C10: lara_col_roll
0x00429CB0: lara_col_roll2
0x00429D80: lara_col_special
----------: lara_col_usemidas
----------: lara_col_diemidas
0x00429DA0: lara_col_swandive
0x00429E10: lara_col_fastdive
----------: lara_col_gymnast
----------: lara_col_waterout
----------: lara_col_laratest1
----------: lara_col_laratest2
----------: lara_col_laratest3
0x00429E90: lara_col_wade
----------: lara_col_twist
0x0042A000: lara_default_col
0x0042A040: + lara_col_jumper
0x0042A120: lara_col_kick
----------: lara_col_deathslide
0x0042A130: * GetLaraCollisionInfo
0x0042A170: lara_slide_slope
0x0042A260: LaraHitCeiling
0x0042A2D0: LaraDeflectEdge
0x0042A350: * LaraDeflectEdgeJump
0x0042A4D0: LaraSlideEdgeJump
0x0042A5C0: TestWall
0x0042A6D0: LaraTestHangOnClimbWall
0x0042A7E0: LaraTestClimbStance
0x0042A8A0: LaraHangTest
0x0042AC00: LaraTestEdgeCatch
0x0042ACB0: LaraTestHangJumpUp
0x0042AE20: LaraTestHangJump
0x0042AFC0: TestHangSwingIn
0x0042B080: TestLaraVault
0x0042B370: TestLaraSlide
0x0042B4A0: LaraFloorFront
0x0042B520: * LaraLandedBad
0x0042B5E0: * GetLaraJointAbsPosition
0x0042B970: GetLJAInt
game/lara1gun.cpp
0x0042BC90: draw_shotgun_meshes
0x0042BCD0: undraw_shotgun_meshes
0x0042BD00: ready_shotgun
0x0042BD70: + RifleHandler
0x0042BE70: + FireShotgun
0x0042BF70: + FireM16
0x0042BFF0: + FireHarpoon
0x0042C180: ControlHarpoonBolt
0x0042C4D0: + FireRocket
0x0042C5C0: + ControlRocket
0x0042C9D0: draw_shotgun
0x0042CB40: undraw_shotgun
0x0042CC50: * AnimateShotgun
game/lara2gun.cpp
0x0042D000: set_pistol_arm
0x0042D050: draw_pistols
0x0042D0D0: undraw_pistols
0x0042D300: ready_pistols
0x0042D360: draw_pistol_meshes
0x0042D3B0: undraw_pistol_mesh_left
0x0042D3F0: undraw_pistol_mesh_right
0x0042D430: + PistolHandler
0x0042D5C0: * AnimatePistols
game/laraclimb.cpp
0x0042D8F0: lara_as_climbleft
0x0042D930: lara_as_climbright
0x0042D970: lara_as_climbstnc
0x0042D9F0: lara_as_climbing
0x0042DA10: lara_as_climbend
0x0042DA30: lara_as_climbdown
0x0042DA50: lara_col_climbleft
0x0042DAB0: lara_col_climbright
0x0042DB10: lara_col_climbstnc
0x0042DD20: lara_col_climbing
----------: lara_col_climbend
0x0042DE70: lara_col_climbdown
0x0042E010: LaraCheckForLetGo
0x0042E0C0: LaraTestClimb
0x0042E330: LaraTestClimbPos
0x0042E400: LaraDoClimbLeftRight
0x0042E4F0: LaraTestClimbUpPos
game/larafire.cpp
0x0042E740: * LaraGun
0x0042ECB0: * CheckForHoldingState
0x0042ECF0: * InitialiseNewWeapon
0x0042EE30: * LaraTargetInfo
0x0042EFD0: * LaraGetNewTarget
0x0042F1F0: * find_target_point
0x0042F2A0: * AimWeapon
0x0042F370: * FireWeapon
0x0042F6E0: * HitTarget
0x0042F780: * SmashItem
0x0042F7E0: * WeaponObject
game/laraflare.cpp
0x0042F840: DoFlareLight
0x0042F8E0: DoFlareInHand
0x0042F9C0: + DrawFlareInAir
0x0042FAC0: CreateFlare
0x0042FCA0: set_flare_arm
0x0042FCF0: draw_flare
0x0042FE60: undraw_flare
0x00430090: draw_flare_meshes
0x004300B0: undraw_flare_meshes
0x004300D0: ready_flare
0x00430110: FlareControl
game/laramisc.cpp
0x00430380: + LaraControl
0x00430A10: * AnimateLara
0x00430D10: + UseItem
0x00430ED0: + LaraCheatGetStuff
0x00430F90: + ControlLaraExtra
0x00430FB0: + InitialiseLaraLoad
0x00430FE0: * InitialiseLara
0x004312A0: + InitialiseLaraInventory
0x00431610: * LaraInitialiseMeshes
game/larasurf.cpp
0x00431710: * LaraSurface
0x00431870: lara_as_surfswim
0x004318E0: lara_as_surfback
0x00431940: lara_as_surfleft
0x004319A0: lara_as_surfright
0x00431A00: lara_as_surftread
0x00431AC0: lara_col_surfswim
0x00431B00: lara_col_surfback
0x00431B30: lara_col_surfleft
0x00431B60: lara_col_surfright
0x00431B90: lara_col_surftread
0x00431BF0: LaraSurfaceCollision
0x00431CF0: LaraTestWaterStepOut
0x00431DE0: LaraTestWaterClimbOut
game/laraswim.cpp
0x00432000: * LaraUnderWater
0x00432230: + SwimTurn
0x004322C0: + lara_as_swim
0x00432330: lara_as_glide
0x004323B0: lara_as_tread
0x00432440: lara_as_dive
0x00432460: lara_as_uwdeath
0x004324C0: lara_as_waterroll
0x004324D0: lara_col_swim
----------: lara_col_glide
----------: lara_col_tread
----------: lara_col_dive
0x004324F0: lara_col_uwdeath
----------: lara_col_waterroll
0x00432550: * GetWaterDepth
0x004326F0: LaraTestWaterDepth
0x004327C0: LaraSwimCollision
0x00432920: LaraWaterCurrent
game/lot.cpp
0x00432B10: InitialiseLOTarray
0x00432B70: * DisableBaddieAI
0x00432BC0: * EnableBaddieAI
0x00432D70: InitialiseSlot
0x00432F80: CreateZone
0x00433040: ClearLOT
game/missile.cpp
0x00433090: + ControlMissile
0x00433360: ShootAtLara
0x00433410: * ExplodingDeath
0x004337A0: ControlBodyPart
game/moveblock.cpp
0x004339A0: InitialiseMovingBlock
0x004339D0: * MovableBlock
0x00433B20: MovableBlockCollision
0x00433D80: TestBlockMovable
0x00433DD0: TestBlockPush
0x00433F20: TestBlockPull
0x00434160: * AlterFloorHeight
0x00434220: DrawMovableBlock
0x00434250: * DrawUnclippedItem
game/objects.cpp
0x004342C0: EarthQuake
0x004343A0: ControlCutShotgun
0x004343E0: InitialiseFinalLevel
0x004344B0: FinalLevelCounter
0x004346C0: MiniCopterControl
0x004347A0: InitialiseDyingMonk
0x00434820: DyingMonk
0x004348B0: ControlGongBonger
0x00434970: DeathSlideCollision
0x00434A30: ControlDeathSlide
0x00434CC0: BigBowlControl
0x00434DB0: BellControl
0x00434E30: InitialiseWindow
0x00434EB0: * SmashWindow
0x00434F80: WindowControl
0x00435020: SmashIceControl
0x00435100: ShutThatDoor
0x00435150: OpenThatDoor
0x00435190: InitialiseDoor
0x00435570: DoorControl
0x00435640: OnDrawBridge
0x00435700: DrawBridgeFloor
0x00435740: DrawBridgeCeiling
0x00435780: DrawBridgeCollision
0x004357B0: InitialiseLift
0x004357F0: LiftControl
0x004358D0: LiftFloorCeiling
0x00435A50: LiftFloor
0x00435A90: LiftCeiling
0x00435AD0: BridgeFlatFloor
0x00435AF0: BridgeFlatCeiling
0x00435B10: GetOffset
0x00435B50: BridgeTilt1Floor
0x00435B80: BridgeTilt1Ceiling
0x00435BC0: BridgeTilt2Floor
0x00435BF0: BridgeTilt2Ceiling
0x00435C30: CopterControl
0x00435D40: GeneralControl
0x00435E20: DetonatorControl
game/people.cpp
0x00435EB0: Targetable
0x00435F40: ControlGlow
0x00435F80: ControlGunShot
0x00435FD0: + GunShot
0x00436040: + GunHit
0x00436100: GunMiss
0x004361B0: ShotLara
0x00436380: * InitialiseCult1
0x004363D0: * Cult1Control
0x00436800: * InitialiseCult3
0x00436850: * Cult3Control
0x00436DC0: * Worker1Control
0x004371C0: * Worker2Control
0x00437620: * BanditControl
0x00437960: * Bandit2Control
0x00437DA0: * WinstonControl
game/pickup.cpp
0x00437F20: * PickUpCollision
0x004383A0: SwitchCollision
0x004385B0: SwitchCollision2
0x004386B0: DetonatorCollision
0x004388F0: KeyHoleCollision
0x00438B30: * PuzzleHoleCollision
0x00438DF0: SwitchControl
0x00438E30: SwitchTrigger
0x00438EF0: KeyTrigger
0x00438F30: PickupTrigger
0x00438F70: SecretControl
game/rat.cpp
0x00438FA0: * MouseControl
game/savegame.cpp
0x00439190: + InitialiseStartInfo
0x00439200: + ModifyStartInfo
0x004392E0: + CreateStartInfo
0x004394F0: + CreateSaveGameInfo
0x00439A20: + ExtractSaveGameInfo
0x0043A280: + ResetSG
0x0043A2A0: + WriteSG
0x0043A2F0: + ReadSG
game/setup.cpp
0x0043A330: * InitialiseLevel
0x0043A490: InitialiseGameFlags
0x0043A500: + InitialiseLevelFlags
0x0043A530: + BaddyObjects
0x0043B570: * TrapObjects
0x0043BB70: * ObjectObjects
0x0043C7C0: + InitialiseObjects
0x0043C830: GetCarriedItems
game/shark.cpp
0x0043C900: * JellyControl
0x0043CA20: * BaracuddaControl
0x0043CC50: * SharkControl
game/skidoo.cpp
0x0043CEE0: * InitialiseSkidoo
0x0043CF20: SkidooCheckGeton
0x0043D010: SkidooCollision
0x0043D110: SkidooBaddieCollision
0x0043D310: TestHeight
0x0043D3D0: DoShift
0x0043D650: DoDynamics
0x0043D6B0: GetCollisionAnim
0x0043D740: + DoSnowEffect
0x0043D880: SkidooDynamics
0x0043DD20: SkidooUserControl
0x0043DEE0: SkidooCheckGetOffOK
0x0043DFF0: SkidooAnimation
0x0043E2D0: + SkidooExplode
0x0043E350: SkidooCheckGetOff
0x0043E590: + SkidooGuns
0x0043E6B0: SkidooControl
0x0043EB10: + DrawSkidoo
0x0043EDF0: * InitialiseSkidman
0x0043EE80: * SkidManControl
0x0043F280: SkidmanPush
0x0043F3A0: * SkidmanCollision
game/sound.cpp
0x0043F430: + GetRealTrack
0x0043F470: * PlaySoundEffect
0x0043F910: * StopSoundEffect
0x0043F970: * SOUND_EndScene
0x0043FA00: * SOUND_Stop
0x0043FA30: + SOUND_Init
game/sphere.cpp
0x0043FA60: TestCollision
0x0043FB90: GetSpheres
0x0043FE70: * GetJointAbsPosition
0x00440010: * BaddieBiteEffect
game/spider.cpp
0x00440070: SpiderLeap
0x00440120: * SpiderControl
0x00440340: * BigSpiderControl
game/text.cpp
0x00440500: + T_InitPrint
0x00440530: + T_Print
0x00440640: + T_ChangeText
0x00440680: + T_SetScale
0x004406A0: + T_FlashText
0x004406D0: + T_AddBackground
0x00440760: + T_RemoveBackground
0x00440770: + T_AddOutline
0x004407A0: + T_RemoveOutline
0x004407B0: + T_CentreH
0x004407D0: + T_CentreV
0x004407F0: + T_RightAlign
0x00440810: + T_BottomAlign
0x00440830: + T_GetTextWidth
0x00440940: + T_RemovePrint
0x00440970: + T_GetStringLen
0x004409A0: + T_DrawText
0x004409D0: + T_DrawTextBox
0x00440B60: + T_DrawThisText
0x00440F40: + GetTextScaleH
0x00440F80: + GetTextScaleV
game/traps.cpp
0x00440FC0: + MineControl
0x004411C0: + ControlSpikeWall
0x00441300: + ControlCeilingSpikes
0x00441420: + HookControl
0x004414B0: PropellerControl
0x00441640: + SpinningBlade
0x004417C0: + IcicleControl
0x004418C0: + InitialiseBlade
0x00441900: + BladeControl
0x004419A0: + InitialiseKillerStatue
0x004419F0: + KillerStatueControl
0x00441B00: SpringBoardControl
0x00441BE0: InitialiseRollingBall
0x00441C20: RollingBallControl
0x00441F70: RollingBallCollision
0x004421C0: SpikeCollision
0x00442320: TrapDoorControl
0x00442370: TrapDoorFloor
0x004423B0: TrapDoorCeiling
0x004423F0: OnTrapDoor
0x004424A0: + Pendulum
0x004425B0: FallingBlock
0x004426C0: FallingBlockFloor
0x00442700: FallingBlockCeiling
0x00442750: + TeethTrap
0x00442810: + FallingCeiling
0x004428F0: + DartEmitterControl
0x00442A30: + DartsControl
0x00442B90: + DartEffectControl
0x00442BE0: + FlameEmitterControl
0x00442C70: + FlameControl
0x00442DE0: + LaraBurn
0x00442E30: + LavaBurn
0x00442F20: LavaSpray
0x00442FF0: ControlLavaBlob
game/yeti.cpp
0x00443100: * GiantYetiControl
0x00443400: * YetiControl
// ======================================
// SPECIFIC
// ======================================
specific/background.cpp
0x00443A40: + BGND_Make640x480
0x00443C00: + BGND_AddTexture
0x00443CC0: + BGND_GetPageHandles
0x00443D00: + BGND_DrawInGameBlack
0x00443D60: + DrawQuad
0x00443E40: + BGND_DrawInGameBackground
0x00444060: + DrawTextureTile
0x004442C0: + BGND_CenterLighting
0x00444570: + BGND_Free
0x004445C0: + BGND_Init
specific/init_3d.cpp
0x004445F0: + Enumerate3DDevices
0x00444620: + D3DCreate
0x00444640: + D3DRelease
0x00444660: + Enum3DDevicesCallback
0x00444720: + D3DIsSupported
0x00444760: + D3DSetViewport
0x00444820: + D3DDeviceCreate
0x004449E0: + Direct3DRelease
0x00444A30: + Direct3DInit
0x00444A40: x unused
0x00444A50: x unused
0x00444A80: x unused
0x00444B50: x unused
0x00444B60: x unused
0x00444BC0: x unused
0x00444BD0: x unused
0x00444C60: x unused
0x00444C70: x unused
specific/winvid.cpp
0x00444C80: + DDrawCreate
0x00444CE0: + DDrawRelease
0x00444D20: + GameWindowCalculateSizeFromClient
0x00444DA0: + GameWindowCalculateSizeFromClientByZero
0x00444E10: + WinVidSetMinWindowSize
0x00444E60: + WinVidClearMinWindowSize
0x00444E70: + WinVidSetMaxWindowSize
0x00444EC0: + WinVidClearMaxWindowSize
0x00444ED0: + CalculateWindowWidth
0x00444F20: + CalculateWindowHeight
0x00444F50: + WinVidGetMinMaxInfo
0x00445060: + WinVidFindGameWindow
0x00445080: + WinVidSpinMessageLoop
0x00445170: + WinVidShowGameWindow
0x004451C0: + WinVidHideGameWindow
0x00445200: + WinVidSetGameWindowSize
0x00445240: + ShowDDrawGameWindow
0x004452F0: + HideDDrawGameWindow
0x00445380: + DDrawSurfaceCreate
0x004453D0: + DDrawSurfaceRestoreLost
0x00445420: + WinVidClearBuffer
0x00445470: + WinVidBufferLock
0x004454B0: + WinVidBufferUnlock
0x004454E0: + WinVidCopyBitmapToBuffer
0x00445570: + GetRenderBitDepth
0x00445600: + WinVidGetColorBitMasks
0x00445680: + BitMaskGetNumberOfBits
0x004456D0: + CalculateCompatibleColor
0x00445740: + WinVidGetDisplayMode
0x004457D0: + WinVidGoFullScreen
0x00445860: + WinVidGoWindowed
0x00445970: + WinVidSetDisplayAdapter
0x00445A50: + CompareVideoModes
0x00445AA0: + WinVidGetDisplayModes
0x00445B00: + EnumDisplayModesCallback
0x00445EC0: + WinVidInit
0x00445F00: + WinVidGetDisplayAdapters
0x00445FB0: + FlaggedStringDelete
0x00445FD0: + EnumerateDisplayAdapters
0x00445FF0: + EnumDisplayAdaptersCallback
0x004461B0: + FlaggedStringsCreate
0x004461F0: + WinVidRegisterGameWindowClass
0x00446260: + WinVidGameWindowProc
0x00446870: + WinVidResizeGameWindow
0x00446A50: + WinVidCheckGameWindowPalette
0x00446B10: + WinVidCreateGameWindow
0x00446BE0: + WinVidFreeWindow
0x00446C10: + WinVidExitMessage
0x00446C60: + WinVidGetDisplayAdapter
0x00446CB0: + WinVidStart
0x00447030: + WinVidFinish
0x00447050: + DisplayModeListInit
0x00447060: + DisplayModeListDelete
0x004470A0: + InsertDisplayMode
0x004470C0: + InsertDisplayModeInListHead
0x00447110: + InsertDisplayModeInListTail
0x00447160: x unused
0x00447170: x unused
0x00447190: x unused
0x004471A0: x unused
0x00447210: x unused
0x00447220: x unused
0x00447260: x unused
0x00447270: x unused
specific/init_input.cpp
0x004472A0: + DInputCreate
0x004472D0: + DInputRelease
0x004472F0: + WinInReadKeyboard
0x00447350: + WinInReadJoystick
0x00447450: x j_WinInputInit
0x00447460: + WinInputInit
0x004474E0: + DInputEnumDevices
0x00447510: + DInputEnumDevicesCallback
0x00447600: + FlaggedStringCreate
0x00447620: + GetJoystick
0x00447670: + DInputKeyboardCreate
0x00447740: + DInputKeyboardRelease
0x00447770: + DInputJoystickCreate
----------: DInputJoystickRelease
0x00447860: + WinInStart
0x00447890: + WinInFinish
0x004478A0: + WinInRunControlPanel
specific/display.cpp
0x004478C0: + IncreaseScreenSize
0x00447930: + DecreaseScreenSize
0x004479A0: + setup_screen_size
0x00447A40: + TempVideoAdjust
0x00447A80: + TempVideoRemove
0x00447AC0: + S_FadeInInventory
0x00447B00: + S_FadeOutInventory
0x00447B30: x unused
0x00447B40: x unused
0x00447B60: x unused
0x00447B70: x unused
0x00447BE0: x unused
0x00447BF0: x unused
0x00447C30: x unused
0x00447C40: x unused
specific/init_sound.cpp
0x00447C70: + GetSoundAdapter
0x00447CC0: + WinSndFreeAllSamples
0x00447CF0: + WinSndMakeSample
0x00447E00: + WinSndIsChannelPlaying
0x00447E50: + WinSndPlaySample
0x00447F40: + WinSndGetFreeChannelIndex
0x00447F80: + WinSndAdjustVolumeAndPan
0x00447FB0: + WinSndAdjustPitch
0x00447FF0: + WinSndStopSample
0x00448050: x j_WinSndInit
0x00448060: + WinSndInit
0x00448100: + DSoundEnumerate
0x00448120: + DSoundEnumCallback
0x00448210: + WinSndStart
0x00448390: + DSoundCreate
0x004483B0: + DSoundBufferTest
0x00448480: + WinSndFinish
0x004484B0: + WinSndIsSoundEnabled
0x004484C0: x j_nullsub_3
0x004484D0: x nullsub_3
specific/init_display.cpp
0x004484E0: + CreateScreenBuffers
0x00448620: + CreatePrimarySurface
0x004486C0: + CreateBackBuffer
0x00448760: + CreateClipper
0x00448800: + CreateWindowPalette
0x004488E0: + CreateZBuffer
0x004489A0: + GetZBufferDepth
0x004489D0: + CreateRenderBuffer
0x00448A80: + CreatePictureBuffer
0x00448AF0: + ClearBuffers
0x00448CA0: + RestoreLostBuffers
0x00448DE0: + UpdateFrame
0x00448EB0: + WaitPrimaryBufferFlip
0x00448EF0: + RenderInit
0x00448F00: + RenderStart
0x004492B0: + RenderFinish
0x004493A0: + ApplySettings
0x004495B0: + FmvBackToGame
0x004496C0: + GameApplySettings
0x00449900: + UpdateGameResolution
0x00449970: + DecodeErrorMessage
specific/file.cpp
0x00449980: + ReadFileSync
0x004499D0: + LoadTexturePages
0x00449B60: + LoadRooms
0x00449F00: + AdjustTextureUVs
0x00449FA0: + LoadObjects
0x0044A520: + LoadSprites
0x0044A660: + LoadItems
0x0044A840: + LoadDepthQ
0x0044A9D0: + LoadPalettes
0x0044AA50: + LoadCameras
0x0044AAB0: + LoadSoundEffects
0x0044AB10: + LoadBoxes
0x0044AD40: + LoadAnimatedTextures
0x0044ADA0: + LoadCinematic
0x0044AE20: + LoadDemo
0x0044AEB0: + LoadDemoExternal
0x0044AF50: LoadSamples
0x0044B1C0: + ChangeFileNameExtension
0x0044B200: + GetFullPath
0x0044B230: + SelectDrive
0x0044B310: + LoadLevel
0x0044B560: + S_LoadLevelFile
0x0044B580: + S_UnloadLevelFile
0x0044B5B0: + S_AdjustTexelCoordinates
0x0044B5D0: + S_ReloadLevelGraphics
0x0044B6A0: + Read_Strings
0x0044B770: + S_LoadGameFlow
specific/fmv.cpp
0x0044BE50: + PlayFMV
0x0044BED0: + WinPlayFMV
0x0044C1B0: + WinStopFMV
0x0044C200: + IntroFMV
specific/frontend.cpp
0x0044C2A0: + S_COLOUR
0x0044C2D0: + S_DrawScreenLine
0x0044C310: + S_DrawScreenBox
0x0044C430: + S_DrawScreenFBox
0x0044C460: + S_FinishInventory
0x0044C470: + S_FadeToBlack
0x0044C4C0: + S_Wait
0x0044C520: + S_PlayFMV
0x0044C530: + S_IntroFMV
specific/game.cpp
0x0044C550: + StartGame
0x0044C6A0: + GameLoop
0x0044C740: + LevelCompleteSequence
0x0044C750: + LevelStats
0x0044C920: + GameStats
0x0044CA40: + GetRandomControl
0x0044CA60: + SeedRandomControl
0x0044CA70: + GetRandomDraw
0x0044CA90: + SeedRandomDraw
0x0044CAA0: + GetValidLevelsList
0x0044CAF0: + GetSavedGamesList
0x0044CB40: + DisplayCredits
0x0044CD80: + S_FrontEndCheck
0x0044CEF0: + S_SaveGame
0x0044D010: + S_LoadGame
specific/hwr.cpp
0x0044D0B0: + HWR_InitState
0x0044D1E0: + HWR_ResetTexSource
0x0044D210: + HWR_ResetColorKey
0x0044D240: + HWR_ResetZBuffer
0x0044D2A0: + HWR_TexSource
0x0044D2D0: + HWR_EnableColorKey
0x0044D320: + HWR_EnableZBuffer
0x0044D3B0: + HWR_BeginScene
0x0044D3E0: + HWR_DrawPolyList
0x0044D560: + HWR_LoadTexturePages
0x0044D5F0: + HWR_FreeTexturePages
0x0044D640: + HWR_GetPageHandles
0x0044D680: + HWR_VertexBufferFull
0x0044D6B0: + HWR_Init
specific/init.cpp
0x0044D6E0: + S_InitialiseSystem
0x0044D730: + ShutdownGame
0x0044D750: + init_game_malloc
0x0044D780: + game_malloc
0x0044D800: + game_free
0x0044D840: + CalculateWibbleTable
0x0044D930: + S_SeedRandom
specific/input.cpp
0x0044D9B0: + Key
0x0044DAD0: + S_UpdateInput
specific/winmain.cpp
0x0044E5A0: + RenderErrorBox
0x0044E5E0: + WinMain
0x0044E7A0: x nullsub_4
0x0044E7B0: + Init
0x0044E830: + WinCleanup
0x0044E860: + WinGameStart
0x0044E8E0: + WinGameFinish
0x0044E920: x ThrowException
0x0044E940: x type_info::operator=(type_infoconst&)
0x0044E950: + S_ExitSystem
specific/screenshot.cpp
0x0044E9A0: + ScreenShotPCX
0x0044EAB0: + CompPCX
0x0044EB60: + EncodeLinePCX
0x0044EC40: + EncodePutPCX
0x0044EC80: + ScreenShot
specific/option.cpp
0x0044EE80: + do_inventory_options
0x0044EF90: + do_passport_option
----------: + do_gamma_option
0x0044F5E0: + do_detail_option
0x0044F8C0: + do_sound_option
0x0044FD60: + do_compass_option
0x0044FE20: + FlashConflicts
0x0044FEA0: + DefaultConflict
0x0044FEE0: + do_control_option
0x004505F0: + S_ShowControls
0x00450AC0: + S_ChangeCtrlText
0x00450B60: + S_RemoveCtrlText
specific/output.cpp
0x00450BA0: + GetRenderHeight
0x00450BB0: + GetRenderWidth
0x00450BC0: + S_InitialisePolyList
0x00450CB0: + S_DumpScreen
0x00450CF0: + S_ClearScreen
0x00450D00: + S_InitialiseScreen
0x00450D40: + S_OutputPolyList
0x00450D80: + S_GetObjectBounds
0x00450FF0: + S_InsertBackPolygon
0x00451040: + S_PrintShadow
0x00451240: + S_CalculateLight
0x00451540: + S_CalculateStaticLight
0x00451580: + S_CalculateStaticMeshLight
0x004516B0: + S_LightRoom
0x004518C0: + S_DrawHealthBar
0x00451A90: + S_DrawAirBar
0x00451C90: + AnimateTextures
0x00451D50: + S_SetupBelowWater
0x00451DB0: + S_SetupAboveWater
0x00451DE0: + S_AnimateTextures
0x00451EA0: + S_DisplayPicture
0x00451FB0: + S_SyncPictureBufferPalette
0x00452030: + S_DontDisplayPicture
0x00452040: + ScreenDump
0x00452050: + ScreenPartialDump
0x00452060: + FadeToPal
0x00452230: + ScreenClear
0x00452260: + S_CopyScreenToBuffer
0x00452310: + S_CopyBufferToScreen
0x00452360: + DecompPCX
0x00452460: x j_nullsub_6
0x00452470: x nullsub_6
specific/setupdlg.cpp
0x00452480: + OpenGameRegistryKey
0x004524D0: + CloseGameRegistryKey
0x004524E0: + SE_WriteAppSettings
0x00452760: + SE_ReadAppSettings
0x00452AC0: + SE_GraphicsTestStart
0x00452B90: + SE_GraphicsTestFinish
0x00452BB0: + SE_GraphicsTestExecute
0x00452BC0: + SE_GraphicsTest
0x00452C20: + SE_DefaultGraphicsSettings
0x00452D70: + SE_SoundTestStart
0x00452E30: + SE_SoundTestFinish
0x00452E40: + SE_SoundTestExecute
0x00452E80: + SE_SoundTest
0x00452EE0: + SE_PropSheetCallback
0x00452F20: + SE_NewPropSheetWndProc
0x00452F80: + SE_ShowSetupDialog
0x00453150: + SE_GraphicsDlgProc
0x00453560: + SE_GraphicsDlgFullScreenModesUpdate
0x00453750: + SE_GraphicsAdapterSet
0x00453770: + SE_GraphicsDlgUpdate
0x00453EB0: + SE_GraphicsDlgInit
0x00454030: + SE_SoundDlgProc
0x004541C0: + SE_SoundAdapterSet
0x004541D0: + SE_SoundDlgUpdate
0x004542F0: + SE_SoundDlgInit
0x004543B0: + SE_ControlsDlgProc
0x004544C0: + SE_ControlsJoystickSet
0x004544D0: + SE_ControlsDlgUpdate
0x00454540: + SE_ControlsDlgInit
0x00454600: + SE_OptionsDlgProc
0x00454690: + SE_OptionsDlgUpdate
0x004548D0: + SE_OptionsStrCat
0x00454920: + SE_AdvancedDlgProc
0x00454A30: + SE_AdvancedDlgUpdate
0x00454AE0: + SE_AdvancedDlgInit
0x00454AF0: + SE_FindSetupDialog
specific/smain.cpp
0x00454B10: + GameMain
0x00454DE0: + TitleSequence
0x00454EF0: + CheckCheatMode
0x00455250: + S_SaveSettings
0x004552D0: + S_LoadSettings
specific/sndpc.cpp
0x004553B0: + S_SoundPlaySample
0x00455400: + S_Sound_CalculateSampleVolume
0x00455430: + S_Sound_CalculateSamplePan
0x00455460: + S_SoundPlaySampleLooped
0x004554B0: + S_SoundSetPanAndVolume
0x004554F0: + S_SoundSetPitch
0x00455510: + S_SoundSetMasterVolume
0x00455520: + S_SoundStopSample
0x00455540: + S_SoundStopAllSamples
0x00455550: + S_SoundSampleIsPlaying
0x00455570: + CD_Init
0x00455600: + CD_Cleanup
0x00455640: + S_CDLoop
0x004556E0: + S_CDPlay
0x00455760: + S_CDStop
0x004557A0: + StartSyncedAudio
0x00455830: + S_CDGetLoc
0x004558A0: + S_CDVolume
specific/texture.cpp
0x00455990: + CopyBitmapPalette
0x00455AD0: + FindNearestPaletteEntry
0x00455BA0: + SyncSurfacePalettes
0x00455C50: + CreateTexturePalette
0x00455CE0: + GetFreePaletteIndex
0x00455D00: + FreePalette
0x00455D30: + SafeFreePalette
0x00455D80: + CreateTexturePage
0x00455DF0: + GetFreeTexturePageIndex
0x00455E10: + CreateTexturePageSurface
0x00455EB0: + TexturePageInit
0x00456030: + Create3DTexture
0x00456060: + SafeFreeTexturePage
0x00456080: + FreeTexturePage
0x004560C0: + TexturePageReleaseVidMemSurface
0x00456100: + FreeTexturePages
0x00456130: + LoadTexturePage
0x004561E0: + ReloadTextures
0x00456220: + GetTexturePageHandle
0x00456260: + AddTexturePage8
0x00456360: + AddTexturePage16
0x00456500: + EnumTextureFormatsCallback
0x00456620: + EnumerateTextureFormats
0x00456650: + CleanupTextures
0x00456660: + InitTextures
specific/utils.cpp
0x00456680: + UpdateTicks
0x004566C0: + TIME_Init
0x00456720: + Sync
0x00456780: + LoadResource
0x004567C0: + UT_InitAccurateTimer
0x00456820: + UT_Microseconds
0x00456870: + UT_CenterWindow
0x004568E0: + UT_FindArg
0x00456910: + UT_MessageBox
0x00456930: + UT_ErrorBox
0x00456980: + CD_NoteAlert
0x004569B0: + DialogBoxProc
specific/registry.cpp
0x00456A20: + GuidBinaryToString
0x00456A80: + GuidStringToBinary
0x00456B30: + OpenRegistryKey
0x00456B60: + IsNewRegistryKeyCreated
0x00456B70: + CloseRegistryKey
0x00456B80: + SetRegistryDwordValue
0x00456BA0: + SetRegistryBoolValue
0x00456BD0: + SetRegistryFloatValue
0x00456C10: + SetRegistryBinaryValue
0x00456C50: + SetRegistryStringValue
0x00456CA0: + DeleteRegistryValue
0x00456CC0: + GetRegistryDwordValue
0x00456D20: + GetRegistryBoolValue
0x00456DA0: + GetRegistryFloatValue
0x00456E00: + GetRegistryBinaryValue
0x00456E80: + GetRegistryStringValue
0x00456F20: + GetRegistryGuidValue
specific/setupwnd.cpp
0x00456FC0: + SE_ReleaseBitmapResource
0x00457000: + SE_LoadBitmapResource
0x004570D0: + SE_DrawBitmap
0x00457140: + SE_UpdateBitmapPalette
0x00457160: + SE_ChangeBitmapPalette
0x004571C0: + SE_RegisterSetupWindowClass
0x00457230: + SE_SetupWindowProc
0x00457470: + SE_PassMessageToImage
specific/utils.cpp
0x004574A0: + UT_MemBlt
// ======================================
// ASM
// ======================================
asm/3d_gen_a.s -> 3dsystem/3d_gen.cpp
----------: + phd_PopMatrix
0x00457510: + phd_PushMatrix
0x0045752E: + phd_PushUnitMatrix
asm/3d_xouta.s -> 3dsystem/3d_out.cpp
0x00457564: + flatA
0x004575C5: + transA
0x004576FF: + gourA
0x0045785F: + gtmapA
0x00457B5C: + wgtmapA
asm/phd_math.s -> 3dsystem/3dinsert.cpp
0x00457EA0: + phd_atan
0x00457EE8: + phd_cos
0x00457EEE: + phd_sin
0x00457F09: x phd_sin_quadrant1
0x00457F23: + phd_sqrt
0x00457F50:
================================================
FILE: binaries/README.md
================================================
# Tomb Raider 2 binaries
Here are few binary files that are taken from the original game and slightly modified to work with TR2Main.
- [Tomb2_original](Tomb2_original.zip) is the original executable build on December 1, 1997 by Core Design (though file timestamp is 2:36pm, December 10, 1997). It does not use *TRMain.dll*. This one is uploaded only so that you can **compare what has changed** in the patched versions.
- [Tomb2_legacy](Tomb2_legacy.zip) is based on the original executable, but it has a slightly enlarged *.text* section with *write* permission. The entry point is redirected to a function that links with *TRMain.dll*, and then jumps to a common entry point. This executable can be run with or without *TRMain.dll*. Recommended for **debugging and unit testing**.
- [Tomb2_clean](Tomb2_clean.zip) is based on the original executable, it has an original entry point and *.text* section, but with *write* permission. It links with *TRMain.dll* through *.import* table. *Winplay.dll* dependency removed. Also it has the compatibility manifest and XP manifest in the *.rsrc* section. This executable requires *TRMain.dll*. Recommended for **release builds**.
Also you can find here some additional media files
- [BAREFOOT.SFX](BAREFOOT.SFX.zip) is an SFX package with 4 sound effects of barefoot steps. These effects are taken from the PlayStation version of the game and slightly remastered by me (only the high-frequency range was restored).
If you are looking for a complete package with the *Tomb2.exe*, *TR2Main.dll* and other stuff, then you should go to [releases](https://github.com/Arsunt/TR2Main/releases).
================================================
FILE: configs/TR2Main.json
================================================
{
"comment": [
" ",
" This is mod configuration file for TR2Main. You can edit it, ",
" if you want to create your own mod of the Tomb Raider II game. ",
" This is JSON format, it's very simple. Here is info about fields: ",
" ",
" - 'comment' is used for comments only and it's ignored by the game. ",
" You can write some notes inside it, like this one. ",
" ",
" - 'default' stores a set of default parameters for all levels. ",
" ",
" - 'levels' stores sets of parameters for each of the levels. ",
" If some parameter is set both in the 'default' and the 'levels', ",
" the parameter from 'levels' is applied. ",
" ",
" - 'filename' is used to set the name of the level file for which ",
" this set of parameters will be used. Just a filename with no path ",
" and no extension. ",
" ",
" - 'picture' is used to set the name of the image file for the level ",
" loading screen. Just a filename with no path and no extension. ",
" ",
" - 'watercolor' is used to set the water color for the level. ",
" It must be in hexadecimal format: RRGGBB (red, green, blue). ",
" PC HW Renderer uses '80C0FF', and PC SW renderer uses 'AAAAFF'. ",
" PS1 colors: '80FFFF', 'B2E6E6', 'CCFFCC', 'CCFF98', 'CCFF80'. ",
" This setting affects only hardware renderer mode. ",
" ",
" - 'barefoot' is used to indicate levels where Lara does not wear ",
" boots. It is used for alternative sound effects of Lara's steps. ",
" The parameter can be true or false. Default value is false. ",
" ",
" - 'semitransparent' is used for semitransparency parameters. ",
" It can contain: 'animtex', 'objects', 'statics', 'rooms' ",
" ",
" - 'animtex' is used to mark animated textures as semitransparent. ",
" The field must be inside 'semitransparent' section. ",
" It can be one of the values: 'auto', 'none', 'all'. Or you can ",
" mark specific animated texture ranges, if you set it as list. ",
" For example, if you want to mark as semitransparent animated ",
" texture ranges [0,1,2,5], just set 'animtex' to '0-2, 5'. ",
" Default value is 'auto'. This means if some room texture is ",
" animated and it has some transparent pixels, it's automatically ",
" marked as semitransparent. If texture is opaque, it stays opaque. ",
" Almost all animated textures of the original game affected by ",
" 'auto' are water surfaces, so it's recommended to use this mode. ",
" ",
" - 'objects' is used to mark object textures as semitransparent ",
" or to mark object polygons as reflective. The field must be ",
" inside 'semitransparent' or 'reflective' section accordingly. ",
" It must contain 'object' and 'meshes' subfields. ",
" 'object' contains the object ID (from 0 to 264), ",
" 'meshes' contains the list of meshes, each contains 'mesh' field ",
" (counts inside the object, starting from zero). Optionally, mesh ",
" contains fields 'filter', 't4list', 't3list', 'c4list', 'c3list'. ",
" 'filter' is used to check that this mesh is really the one that ",
" supposed to be patched. Fields 'v', 't4', 't3', 'c4', 'c3' set ",
" vertices count, textured quads/triangles, colored quads/triangles ",
" numbers accordingly. If some of these fields is not set, it is ",
" considered as zero. If there is no 'filter' or 'v' is zero, the ",
" mesh check is disabled. 't4list', 't3list', 'c4list', 'c3list' ",
" stores lists of polygons for textured quads/triangles, colored ",
" quads/triangles accordingly. Format is the same as for 'animtex', ",
" but there is no 'auto' option. If there are no lists in the mesh, ",
" then every list considered as 'all'. If there are some lists, but ",
" some absent, every missing list considered as 'none'. The listed ",
" polygons become semitransparent or reflective. ",
" ",
" - 'statics' is used to mark static mesh textures as semitransparent ",
" or to mark static polygons as reflective. The field must be ",
" inside 'semitransparent' or 'reflective' section accordingly. ",
" It must contain 'static' subfield for static ID (counts from 0). ",
" All other subfields are identical to 'meshes' described in the ",
" 'objects', but 'mesh' subfield is not used, because every static ",
" is a single mesh itself. Note that most static objects meshes ",
" don't have normal vectors, required for reflections, so even if ",
" you set them reflective, they won't become reflective. ",
" ",
" - 'rooms' is used to mark room mesh textures as semitransparent. ",
" The field must be inside 'semitransparent' section. ",
" It must contain 'room' subfield for room ID (counts from 0). ",
" All other subfields are identical to 'meshes' described in the ",
" 'objects', but 'mesh' subfield is not used, because every room ",
" is a single mesh itself. Note that it is not necessary to list ",
" all semitransparent polygons for all rooms if they refer to the ",
" same texture. Just specify a few polygons in several rooms. ",
" Room meshes have no normal vectors, so they cannot be reflective. ",
" "
],
"default": {
"comment": "Default settings for all levels",
"watercolor": "80FFFF",
"semitransparent": {
"animtex": "auto",
"objects": [{
"comment": "Windshield of a red fast snowmobile",
"object": 13,
"meshes": [{
"mesh": 0,
"filter": {"v":59, "t4":14, "t3":104},
"t3list": "48-51, 54-71, 73-78"
}]
}, {
"comment": "Sunglasses lenses in the menu (Details option)",
"object": 153,
"meshes": [{
"mesh": 0,
"filter": {"v":80, "t4":66, "t3":4, "c4":2},
"t4list": "23-30, 44-51",
"t3list": "all"
}]
}, {
"comment": "Sphere of Doom 1 (Dragon transformation)",
"object": 209,
"meshes": [{"mesh": 0}]
}, {
"comment": "Sphere of Doom 2 (Dragon transformation)",
"object": 210,
"meshes": [{"mesh": 0}]
}, {
"comment": "Flare fire",
"object": 235,
"meshes": [{"mesh": 0}]
}, {
"comment": "Pistols/uzi/magnums gunfire flash",
"object": 240,
"meshes": [{"mesh": 0}]
}, {
"comment": "M16 gunfire flash",
"object": 241,
"meshes": [{"mesh": 0}]
}]
},
"reflective": {
"objects": [{
"comment": "Windshield of a gray fast snowmobile (The Golden Mask)",
"object": 13,
"meshes": [{
"mesh": 0,
"filter": {"v":59, "t4":14, "t3":73, "c3":17},
"t3list": "48",
"c3list": "all"
}]
}, {
"comment": "Flamethrower mask",
"object": 34,
"meshes": [{
"mesh": 15,
"filter": {"v":38, "t4":30, "t3":12},
"t4list": "22-26"
}]
}, {
"comment": "Windshield of an armed snowmobile",
"object": 51,
"meshes": [{
"mesh": 0,
"filter": {"v":88, "t4":45, "t3":60},
"t4list": "21-22",
"t3list": "34-47"
}]
}, {
"comment": "Rolling wheel blade",
"object": 80,
"meshes": [{
"mesh": 0,
"t4list": "all",
"c4list": "all"
}]
}, {
"comment": "Wall mounted blade",
"object": 81,
"meshes": [{"mesh": 1}]
}, {
"comment": "Sword of killing statue",
"object": 82,
"meshes": [{"mesh": 7}]
}]
}
},
"levels": [{
"comment": "Title",
"filename": "TITLE"
}, {
"comment": "Lara's Home",
"filename": "ASSAULT",
"picture": "MANSION",
"semitransparent": {
"statics": [{
"comment": "Glass on the sink in the bathroom",
"static": 21,
"filter": {"v":46, "t4":41},
"t4list": "17-25"
}]
}
}, {
"comment": "The Great Wall",
"filename": "WALL",
"picture": "CHINA",
"watercolor": "B2E6E6"
}, {
"comment": "Venice",
"filename": "BOAT",
"picture": "VENICE",
"watercolor": "CCFF80",
"semitransparent": {
"rooms": [{
"comment": "Chain-link fence",
"room": 0,
"filter": {"v":112, "t4":91},
"t4list": "3"
}]
},
"reflective": {
"objects": [{
"comment": "Boathouse key",
"object": 197,
"meshes": [{
"mesh": 0,
"filter": {"v":66, "t4":53},
"t4list": "0-9, 11-21, 24-38, 44-51"
}]
}]
}
}, {
"comment": "Bartoli's Hideout",
"filename": "VENICE",
"picture": "VENICE",
"watercolor": "CCFF80",
"semitransparent": {
"rooms": [{
"comment": "Chain-link fence",
"room": 15,
"filter": {"v":30, "t4":22},
"t4list": "19, 21"
}]
},
"reflective": {
"objects": [{
"comment": "Library key",
"object": 197,
"meshes": [{
"mesh": 0,
"filter": {"v":66, "t4":53},
"t4list": "0-9, 11-21, 24-38, 44-51"
}]
}]
}
}, {
"comment": "Opera House",
"filename": "OPERA",
"picture": "VENICE",
"watercolor": "CCFF80",
"semitransparent": {
"rooms": [{
"comment": "Chain-link fence",
"room": 104,
"filter": {"v":43, "t4":29},
"t4list": "2, 4, 6, 8"
}]
}
}, {
"comment": "Offshore Rig",
"filename": "RIG",
"picture": "RIG"
}, {
"comment": "Diving Area",
"filename": "PLATFORM",
"picture": "RIG"
}, {
"comment": "40 Fathoms",
"filename": "UNWATER",
"picture": "TITAN",
"barefoot": true
}, {
"comment": "Wreck of the Maria Doria",
"filename": "KEEL",
"picture": "TITAN",
"barefoot": true,
"semitransparent": {
"rooms": [{
"comment": "Stained glass",
"room": 83,
"filter": {"v":331, "t4":303, "t3":12},
"t4list": "212-216"
}]
}
}, {
"comment": "Living Quarters",
"filename": "LIVING",
"picture": "TITAN",
"barefoot": true
}, {
"comment": "The Deck",
"filename": "DECK",
"picture": "TITAN",
"barefoot": true
}, {
"comment": "Tibetan Foothills",
"filename": "SKIDOO",
"picture": "TIBET",
"watercolor": "B2E6E6"
}, {
"comment": "Barkhang Monastery",
"filename": "MONASTRY",
"picture": "TIBET"
}, {
"comment": "Catacombs of the Talion",
"filename": "CATACOMB",
"picture": "TIBET"
}, {
"comment": "Ice Palace",
"filename": "ICECAVE",
"picture": "TIBET"
}, {
"comment": "Temple of Xian",
"filename": "EMPRTOMB",
"picture": "CHINA",
"watercolor": "CCFF98"
}, {
"comment": "Floating Islands",
"filename": "FLOATING",
"picture": "CHINA",
"watercolor": "CCFFCC"
}, {
"comment": "The Dragon's Lair",
"filename": "XIAN",
"picture": "CHINA",
"watercolor": "CCFFCC"
}, {
"comment": "Home Sweet Home",
"filename": "HOUSE",
"picture": "MANSION",
"barefoot": true,
"semitransparent": {
"statics": [{
"comment": "Glass on the sink in the bathroom",
"static": 21,
"filter": {"v":46, "t4":41},
"t4list": "17-25"
}]
}
}, {
"comment": "Cutscene after The Great Wall",
"filename": "CUT1",
"watercolor": "B2E6E6"
}, {
"comment": "Cutscene after Opera House",
"filename": "CUT2"
}, {
"comment": "Cutscene after Diving Area",
"filename": "CUT3"
}, {
"comment": "Cutscene after Temple of Xian",
"filename": "CUT4"
}, {
"comment": "The Cold War",
"filename": "LEVEL1",
"picture": "LEVEL1"
}, {
"comment": "Fool's Gold",
"filename": "LEVEL2",
"picture": "LEVEL2"
}, {
"comment": "Furnace of the Gods",
"filename": "LEVEL3",
"picture": "LEVEL3"
}, {
"comment": "Kingdom",
"filename": "LEVEL4",
"picture": "LEVEL4"
}, {
"comment": "Nightmare in Vegas",
"filename": "LEVEL5",
"picture": "LEVEL5",
"semitransparent": {
"statics": [{
"comment": "Glass on the sink in the bathroom",
"static": 0,
"filter": {"v":46, "t4":60},
"t4list": "23-32"
}]
}
}]
}
================================================
FILE: configs/textures/texpages/README.md
================================================
# Tomb Raider 2 texture pages configuration
Here is [configuration](config.json) file for the official iOS/Android port texture package. Developers used few dirty tricks to make textures fit. If you create your own texture package properly, you don't need this configuration.
================================================
FILE: configs/textures/texpages/config.json
================================================
{
"comment": [
" ",
" This is texture pages configuration file for TR2Main. You can use ",
" it, if texture pages have wrong design like ones for iOS/Android. ",
" This is JSON format, it's very simple. Here is info about fields: ",
" ",
" - 'comment' is used for comments only and it's ignored by the game. ",
" You can write some notes inside it, like this one. ",
" ",
" - 'default' stores a set of default parameters for all levels. ",
" ",
" - 'levels' stores sets of parameters for each of the levels. ",
" If some parameter is set both in the 'default' and the 'levels', ",
" the parameter from 'levels' is applied. ",
" ",
" - 'filename' is used to set the name of the level file for which ",
" this set of parameters will be used. Just a filename with no path ",
" and no extension. ",
" ",
" - 'legacy_colors' is used to prevent improvement of untextured ",
" polygons colors. ",
" ",
" - 'uv_adjust' is used to force UV adjustment if textures are ",
" misplaced and produce texture bleeding. You can set it to value ",
" between 0.0 and 1.0. Don't use it if your textures are OK. ",
" ",
" - 'glyphs' is used to modify some font sprites parameters. ",
" 'id' contains the glyph ID (from 0 to 109), ",
" 'x_offset' contains the integer value of glyph X adjustment, ",
" 'y_offset' contains the integer value of glyph Y adjustment, ",
" 'x_stretch' contains the float value of glyph horizontal stretch, ",
" 'y_stretch' contains the float value of glyph vertical stretch, ",
" 'spacing' contains the integer value of glyph spacing. ",
" "
],
"default": {
"comment": "Default settings for all levels",
"legacy_colors": false,
"uv_adjust": 1.0,
"glyphs": [{
"comment": "A",
"id": 0,
"spacing": 11
}, {
"comment": "B",
"id": 1,
"spacing": 10
}, {
"comment": "C",
"id": 2,
"spacing": 10
}, {
"comment": "D",
"id": 3,
"spacing": 10
}, {
"comment": "E",
"id": 4,
"spacing": 8
}, {
"comment": "F",
"id": 5,
"spacing": 8
}, {
"comment": "G",
"id": 6,
"spacing": 10
}, {
"comment": "H",
"id": 7,
"spacing": 10
}, {
"comment": "I",
"id": 8,
"spacing": 9
}, {
"comment": "J",
"id": 9,
"spacing": 8
}, {
"comment": "K",
"id": 10,
"spacing": 10
}, {
"comment": "L",
"id": 11,
"spacing": 9
}, {
"comment": "M",
"id": 12,
"spacing": 11
}, {
"comment": "N",
"id": 13,
"spacing": 10
}, {
"comment": "O",
"id": 14,
"spacing": 11
}, {
"comment": "P",
"id": 15,
"spacing": 10
}, {
"comment": "Q",
"id": 16,
"spacing": 11
}, {
"comment": "R",
"id": 17,
"spacing": 10
}, {
"comment": "S",
"id": 18,
"spacing": 10
}, {
"comment": "T",
"id": 19,
"spacing": 10
}, {
"comment": "U",
"id": 20,
"spacing": 10
}, {
"comment": "V",
"id": 21,
"spacing": 11
}, {
"comment": "W",
"id": 22,
"spacing": 11
}, {
"comment": "X",
"id": 23,
"spacing": 11
}, {
"comment": "Y",
"id": 24,
"spacing": 11
}, {
"comment": "Z",
"id": 25,
"spacing": 10
}, {
"comment": "a",
"id": 26,
"spacing": 9
}, {
"comment": "b",
"id": 27,
"spacing": 10
}, {
"comment": "c",
"id": 28,
"spacing": 9
}, {
"comment": "d",
"id": 29,
"spacing": 10
}, {
"comment": "e",
"id": 30,
"spacing": 10
}, {
"comment": "f",
"id": 31,
"spacing": 10
}, {
"comment": "g",
"id": 32,
"y_offset": -6,
"spacing": 10
}, {
"comment": "h",
"id": 33,
"spacing": 9
}, {
"comment": "i",
"id": 34,
"x_stretch": 2.0,
"spacing": 9
}, {
"comment": "j",
"id": 35,
"y_offset": -6,
"spacing": 8
}, {
"comment": "k",
"id": 36,
"spacing": 10
}, {
"comment": "l",
"id": 37,
"x_stretch": 2.0,
"spacing": 9
}, {
"comment": "m",
"id": 38,
"spacing": 10
}, {
"comment": "n",
"id": 39,
"spacing": 9
}, {
"comment": "o",
"id": 40,
"spacing": 10
}, {
"comment": "p",
"id": 41,
"y_offset": -6,
"spacing": 10
}, {
"comment": "q",
"id": 42,
"y_offset": -6,
"spacing": 10
}, {
"comment": "r",
"id": 43,
"spacing": 9
}, {
"comment": "s",
"id": 44,
"spacing": 9
}, {
"comment": "t",
"id": 45,
"spacing": 9
}, {
"comment": "u",
"id": 46,
"spacing": 9
}, {
"comment": "v",
"id": 47,
"spacing": 11
}, {
"comment": "w",
"id": 48,
"spacing": 11
}, {
"comment": "x",
"id": 49,
"spacing": 11
}, {
"comment": "y",
"id": 50,
"y_offset": -6,
"spacing": 11
}, {
"comment": "z",
"id": 51,
"spacing": 9
}, {
"comment": "0",
"id": 52,
"spacing": 10
}, {
"comment": "1",
"id": 53,
"x_stretch": 2.0,
"spacing": 10
}, {
"comment": "2",
"id": 54,
"spacing": 9
}, {
"comment": "3",
"id": 55,
"spacing": 9
}, {
"comment": "4",
"id": 56,
"spacing": 11
}, {
"comment": "5",
"id": 57,
"spacing": 9
}, {
"comment": "6",
"id": 58,
"spacing": 10
}, {
"comment": "7",
"id": 59,
"spacing": 10
}, {
"comment": "8",
"id": 60,
"spacing": 10
}, {
"comment": "9",
"id": 61,
"spacing": 10
}, {
"comment": ".",
"id": 62,
"spacing": 5
}, {
"comment": ",",
"id": 63,
"spacing": 6
}, {
"comment": "!",
"id": 64,
"spacing": 5
}, {
"comment": "?",
"id": 65,
"spacing": 8
}, {
"comment": "[ / double quote",
"id": 66,
"spacing": 6
}, {
"comment": "diaeresis diacritic",
"id": 67,
"spacing": 8
}, {
"comment": "/",
"id": 68,
"spacing": 10
}, {
"comment": "circumflex diacritic",
"id": 69,
"x_offset": 4,
"spacing": 10
}, {
"comment": "acute diacritic",
"id": 70,
"x_offset": 4,
"spacing": 6
}, {
"comment": "-",
"id": 71,
"spacing": 7
}, {
"comment": "+ / =",
"id": 72,
"spacing": 10
}, {
"comment": ":",
"id": 73,
"spacing": 5
}, {
"comment": "eszett",
"id": 74,
"spacing": 10
}, {
"comment": "] / +",
"id": 75,
"spacing": 6
}, {
"comment": "backslash / copyright",
"id": 76,
"spacing": 10
}, {
"comment": "grave diacritic",
"id": 77,
"spacing": 7
}, {
"comment": "# / &",
"id": 78,
"spacing": 11
}, {
"comment": "'",
"id": 79,
"spacing": 6
}, {
"comment": "ring menu arrow: Up",
"id": 80,
"spacing": 13
}, {
"comment": "ring menu arrow: Down",
"id": 81,
"spacing": 13
}, {
"comment": "ammo digit: 0",
"id": 82,
"spacing": 7
}, {
"comment": "ammo digit: 1",
"id": 83,
"spacing": 7
}, {
"comment": "ammo digit: 2",
"id": 84,
"spacing": 7
}, {
"comment": "ammo digit: 3",
"id": 85,
"spacing": 7
}, {
"comment": "ammo digit: 4",
"id": 86,
"spacing": 7
}, {
"comment": "ammo digit: 5",
"id": 87,
"spacing": 7
}, {
"comment": "ammo digit: 6",
"id": 88,
"spacing": 7
}, {
"comment": "ammo digit: 7",
"id": 89,
"y_offset": -2,
"y_stretch": 0.7,
"spacing": 7
}, {
"comment": "ammo digit: 8",
"id": 90,
"y_offset": -2,
"y_stretch": 0.7,
"spacing": 7
}, {
"comment": "ammo digit: 9",
"id": 91,
"y_offset": -2,
"y_stretch": 0.7,
"spacing": 7
}, {
"comment": "button: Unknown",
"id": 92,
"spacing": 15
}, {
"comment": "button: Triangle",
"id": 93,
"spacing": 14
}, {
"comment": "button: Circle",
"id": 94,
"spacing": 14
}, {
"comment": "button: Cross",
"id": 95,
"spacing": 14
}, {
"comment": "button: Square",
"id": 96,
"spacing": 14
}, {
"comment": "button: L1",
"id": 97,
"spacing": 15
}, {
"comment": "button: R1",
"id": 98,
"spacing": 15
}, {
"comment": "button: L2",
"id": 99,
"spacing": 15
}, {
"comment": "button: R2",
"id": 100,
"spacing": 15
}, {
"comment": "volume icon: Music",
"id": 101,
"spacing": 11
}, {
"comment": "volume icon: SFX",
"id": 102,
"spacing": 13
}, {
"comment": "ammo icon: Shotgun",
"id": 103,
"spacing": 7
}, {
"comment": "ammo icon: Magnum",
"id": 104,
"spacing": 7
}, {
"comment": "ammo icon: Uzi",
"id": 105,
"spacing": 7
}, {
"comment": "arrow down",
"id": 106,
"spacing": 10
}, {
"comment": "arrow up",
"id": 107,
"spacing": 10
}, {
"comment": "arrow left",
"id": 108,
"spacing": 12
}, {
"comment": "arrow right",
"id": 109,
"spacing": 12
}]
}
}
================================================
FILE: embedded/BUTTONS.JSON
================================================
{
"esc": {"u": 0, "v": 0, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"f1": {"u": 25, "v": 0, "x": 0, "y": -15, "spacing": 19, "width": 19, "height": 15},
"f2": {"u": 44, "v": 0, "x": 0, "y": -15, "spacing": 19, "width": 19, "height": 15},
"f3": {"u": 63, "v": 0, "x": 0, "y": -15, "spacing": 19, "width": 19, "height": 15},
"f4": {"u": 82, "v": 0, "x": 0, "y": -15, "spacing": 19, "width": 19, "height": 15},
"f5": {"u": 101, "v": 0, "x": 0, "y": -15, "spacing": 19, "width": 19, "height": 15},
"f6": {"u": 120, "v": 0, "x": 0, "y": -15, "spacing": 19, "width": 19, "height": 15},
"f7": {"u": 139, "v": 0, "x": 0, "y": -15, "spacing": 19, "width": 19, "height": 15},
"f8": {"u": 158, "v": 0, "x": 0, "y": -15, "spacing": 19, "width": 19, "height": 15},
"f9": {"u": 177, "v": 0, "x": 0, "y": -15, "spacing": 19, "width": 19, "height": 15},
"f10": {"u": 196, "v": 0, "x": 0, "y": -15, "spacing": 19, "width": 19, "height": 15},
"f11": {"u": 215, "v": 0, "x": 0, "y": -15, "spacing": 19, "width": 19, "height": 15},
"f12": {"u": 234, "v": 0, "x": 0, "y": -15, "spacing": 19, "width": 19, "height": 15},
"tab": {"u": 0, "v": 16, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"1": {"u": 32, "v": 16, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"2": {"u": 48, "v": 16, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"3": {"u": 64, "v": 16, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"4": {"u": 80, "v": 16, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"5": {"u": 96, "v": 16, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"6": {"u": 112, "v": 16, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"7": {"u": 128, "v": 16, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"8": {"u": 144, "v": 16, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"9": {"u": 160, "v": 16, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"0": {"u": 176, "v": 16, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"-": {"u": 192, "v": 16, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"=": {"u": 208, "v": 16, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"backspace": {"u": 224, "v": 16, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"capslock": {"u": 0, "v": 32, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"q": {"u": 32, "v": 32, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"w": {"u": 48, "v": 32, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"e": {"u": 64, "v": 32, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"r": {"u": 80, "v": 32, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"t": {"u": 96, "v": 32, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"y": {"u": 112, "v": 32, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"u": {"u": 128, "v": 32, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"i": {"u": 144, "v": 32, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"o": {"u": 160, "v": 32, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"p": {"u": 176, "v": 32, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"[": {"u": 192, "v": 32, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"]": {"u": 208, "v": 32, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"return": {"u": 224, "v": 32, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"ctrl": {"u": 0, "v": 48, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"a": {"u": 32, "v": 48, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"s": {"u": 48, "v": 48, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"d": {"u": 64, "v": 48, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"f": {"u": 80, "v": 48, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"g": {"u": 96, "v": 48, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"h": {"u": 112, "v": 48, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"j": {"u": 128, "v": 48, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"k": {"u": 144, "v": 48, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"l": {"u": 160, "v": 48, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
";": {"u": 176, "v": 48, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"'": {"u": 192, "v": 48, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"`": {"u": 208, "v": 48, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"shift": {"u": 224, "v": 48, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"alt": {"u": 0, "v": 64, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"\\": {"u": 32, "v": 64, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"z": {"u": 48, "v": 64, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"x": {"u": 64, "v": 64, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"c": {"u": 80, "v": 64, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"v": {"u": 96, "v": 64, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"b": {"u": 112, "v": 64, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"n": {"u": 128, "v": 64, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"m": {"u": 144, "v": 64, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
",": {"u": 160, "v": 64, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
".": {"u": 176, "v": 64, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"/": {"u": 192, "v": 64, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"<": {"u": 208, "v": 64, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"space": {"u": 224, "v": 64, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"printscreen": {"u": 0, "v": 96, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"scrolllock": {"u": 32, "v": 96, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pause": {"u": 64, "v": 96, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"insert": {"u": 0, "v": 112, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"home": {"u": 32, "v": 112, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pageup": {"u": 64, "v": 112, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"delete": {"u": 0, "v": 128, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"end": {"u": 32, "v": 128, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pagedown": {"u": 64, "v": 128, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"left": {"u": 0, "v": 144, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"right": {"u": 16, "v": 144, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"up": {"u": 32, "v": 144, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"down": {"u": 48, "v": 144, "x": 0, "y": -15, "spacing": 15, "width": 15, "height": 15},
"numlock": {"u": 64, "v": 144, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad7": {"u": 128, "v": 96, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad8": {"u": 160, "v": 96, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad9": {"u": 192, "v": 96, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad/": {"u": 224, "v": 96, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad4": {"u": 128, "v": 112, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad5": {"u": 160, "v": 112, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad6": {"u": 192, "v": 112, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad*": {"u": 224, "v": 112, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad1": {"u": 128, "v": 128, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad2": {"u": 160, "v": 128, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad3": {"u": 192, "v": 128, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad-": {"u": 224, "v": 128, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad0": {"u": 128, "v": 144, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad.": {"u": 160, "v": 144, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"enter": {"u": 192, "v": 144, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"pad+": {"u": 224, "v": 144, "x": 0, "y": -15, "spacing": 25, "width": 25, "height": 15},
"di1": {"u": 0, "v": 160, "x": 0, "y": -15, "spacing": 17, "width": 17, "height": 15},
"di2": {"u": 32, "v": 160, "x": 0, "y": -15, "spacing": 17, "width": 17, "height": 15},
"di3": {"u": 64, "v": 160, "x": 0, "y": -15, "spacing": 17, "width": 17, "height": 15},
"di4": {"u": 96, "v": 160, "x": 0, "y": -15, "spacing": 17, "width": 17, "height": 15},
"di5": {"u": 128, "v": 160, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"di6": {"u": 160, "v": 160, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"di7": {"u": 192, "v": 160, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"di8": {"u": 224, "v": 160, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"di9": {"u": 0, "v": 176, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"di10": {"u": 32, "v": 176, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"di11": {"u": 64, "v": 176, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"di12": {"u": 96, "v": 176, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"di13": {"u": 128, "v": 176, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"di14": {"u": 160, "v": 176, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"di15": {"u": 192, "v": 176, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"di16": {"u": 224, "v": 176, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"ps1": {"u": 0, "v": 192, "x": 0, "y": -15, "spacing": 17, "width": 17, "height": 15},
"ps2": {"u": 32, "v": 192, "x": 0, "y": -15, "spacing": 17, "width": 17, "height": 15},
"ps3": {"u": 64, "v": 192, "x": 0, "y": -15, "spacing": 17, "width": 17, "height": 15},
"ps4": {"u": 96, "v": 192, "x": 0, "y": -15, "spacing": 17, "width": 17, "height": 15},
"ps5": {"u": 128, "v": 192, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"ps6": {"u": 160, "v": 192, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"ps7": {"u": 192, "v": 192, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"ps8": {"u": 224, "v": 192, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"ps9": {"u": 0, "v": 208, "x": 0, "y": -15, "spacing": 26, "width": 26, "height": 15},
"ps10": {"u": 32, "v": 208, "x": 0, "y": -15, "spacing": 26, "width": 26, "height": 15},
"ps11": {"u": 64, "v": 208, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"ps12": {"u": 96, "v": 208, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"ps13": {"u": 128, "v": 208, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"ps14": {"u": 160, "v": 208, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"ps15": {"u": 192, "v": 208, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"ps16": {"u": 224, "v": 208, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"xi1": {"u": 0, "v": 224, "x": 0, "y": -15, "spacing": 17, "width": 17, "height": 15},
"xi2": {"u": 32, "v": 224, "x": 0, "y": -15, "spacing": 17, "width": 17, "height": 15},
"xi3": {"u": 64, "v": 224, "x": 0, "y": -15, "spacing": 17, "width": 17, "height": 15},
"xi4": {"u": 96, "v": 224, "x": 0, "y": -15, "spacing": 17, "width": 17, "height": 15},
"xi5": {"u": 128, "v": 224, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"xi6": {"u": 160, "v": 224, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"xi7": {"u": 192, "v": 224, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"xi8": {"u": 224, "v": 224, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"xi9": {"u": 0, "v": 240, "x": 0, "y": -15, "spacing": 26, "width": 26, "height": 15},
"xi10": {"u": 32, "v": 240, "x": 0, "y": -15, "spacing": 26, "width": 26, "height": 15},
"xi11": {"u": 64, "v": 240, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"xi12": {"u": 96, "v": 240, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"xi13": {"u": 128, "v": 240, "x": 0, "y": -15, "spacing": 26, "width": 26, "height": 15},
"xi14": {"u": 160, "v": 240, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"xi15": {"u": 192, "v": 240, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15},
"xi16": {"u": 224, "v": 240, "x": 0, "y": -15, "spacing": 18, "width": 18, "height": 15}
}
================================================
FILE: game/bear.cpp
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/bear.h"
#include "game/box.h"
#include "game/effects.h"
#include "game/lot.h"
#include "specific/game.h"
#include "global/vars.h"
#define BEAR_TOUCH (0x2406C)
typedef enum {
BEAR_STROLL,
BEAR_STOP,
BEAR_WALK,
BEAR_RUN,
BEAR_REAR,
BEAR_ROAR,
BEAR_ATTACK1,
BEAR_ATTACK2,
BEAR_EAT,
BEAR_DEATH
} BEAR_ANIMS;
static const BITE_INFO BearBite = {
0, 96, 335, 14
};
void __cdecl BearControl(__int16 itemID) {
ITEM_INFO *item = &Items[itemID];
if( item->status == ITEM_INVISIBLE ) {
if( !EnableBaddieAI(itemID, FALSE) ) {
return;
}
item->status = ITEM_ACTIVE;
}
CREATURE_INFO *bear = (CREATURE_INFO *)item->data;
if( bear == NULL ) return; // NOTE: additional check not presented in the original game
__int16 angle = 0;
__int16 head = 0;
if( item->hitPoints <= 0 ) {
angle = CreatureTurn(item, 1*PHD_DEGREE);
switch ( item->currentAnimState ) {
case BEAR_STROLL :
case BEAR_RUN :
item->goalAnimState = BEAR_STOP;
break;
case BEAR_REAR :
bear->flags = 1;
item->goalAnimState = BEAR_DEATH;
break;
case BEAR_STOP :
bear->flags = 0;
item->goalAnimState = BEAR_DEATH;
break;
case BEAR_DEATH :
if( bear->flags && CHK_ANY(item->touchBits, BEAR_TOUCH) ) {
LaraItem->hitPoints -= 200;
LaraItem->hit_status = 1;
bear->flags = 0;
}
break;
case BEAR_WALK :
item->goalAnimState = BEAR_REAR;
break;
default :
break;
}
} else {
AI_INFO info;
BOOL isLaraDead;
CreatureAIInfo(item, &info);
if( info.ahead ) {
head = info.angle;
}
CreatureMood(item, &info, TRUE);
angle = CreatureTurn(item, bear->maximum_turn);
isLaraDead = ( LaraItem->hitPoints < 0 );
if( item->hit_status ) {
bear->flags = 1;
}
switch ( item->currentAnimState ) {
case BEAR_STOP :
if( isLaraDead ) {
if( info.bite && info.distance < 0x90000 ) {
item->goalAnimState = BEAR_EAT;
} else {
item->goalAnimState = BEAR_STROLL;
}
} else if( item->requiredAnimState != BEAR_STROLL ) {
item->goalAnimState = item->requiredAnimState;
} else if( bear->mood != MOOD_BORED ) {
item->goalAnimState = BEAR_RUN;
} else {
item->goalAnimState = BEAR_STROLL;
}
break;
case BEAR_STROLL :
bear->maximum_turn = 2*PHD_DEGREE;
if( isLaraDead && CHK_ANY(item->touchBits, BEAR_TOUCH) && info.ahead ) {
item->goalAnimState = BEAR_STOP;
} else if( bear->mood != MOOD_BORED ) {
item->goalAnimState = BEAR_STOP;
if( bear->mood == MOOD_ESCAPE ) {
item->requiredAnimState = BEAR_STROLL;
}
} else if( GetRandomControl() < 0x50 ) {
item->requiredAnimState = BEAR_ROAR;
item->goalAnimState = BEAR_STOP;
}
break;
case BEAR_RUN :
bear->maximum_turn = 910;
if( CHK_ANY(item->touchBits, BEAR_TOUCH) ) {
LaraItem->hitPoints -= 3;
LaraItem->hit_status = 1;
}
if( bear->mood == MOOD_BORED || isLaraDead ) {
item->goalAnimState = BEAR_STOP;
} else if( info.ahead && item->requiredAnimState == BEAR_STROLL ) {
if( !bear->flags && info.distance < 0x400000 && GetRandomControl() < 0x300 ) {
item->requiredAnimState = BEAR_REAR;
item->goalAnimState = BEAR_STOP;
}
else if( info.distance < 0x100000 ) {
item->goalAnimState = BEAR_ATTACK1;
}
}
break;
case BEAR_REAR :
if( bear->flags ) {
item->requiredAnimState = BEAR_STROLL;
item->goalAnimState = BEAR_STOP;
} else if( item->requiredAnimState != BEAR_STROLL ) {
item->goalAnimState = item->requiredAnimState;
} else if( bear->mood == MOOD_BORED || bear->mood == MOOD_ESCAPE ) {
item->goalAnimState = BEAR_STOP;
} else if( info.bite && info.distance < 360000 ) {
item->goalAnimState = BEAR_ATTACK2;
} else {
item->goalAnimState = BEAR_WALK;
}
break;
case BEAR_WALK :
if( bear->flags ) {
item->requiredAnimState = BEAR_STROLL;
item->goalAnimState = BEAR_REAR;
} else if( info.ahead && CHK_ANY(item->touchBits, BEAR_TOUCH) ) {
item->goalAnimState = BEAR_REAR;
} else if( bear->mood == MOOD_ESCAPE ) {
item->goalAnimState = BEAR_REAR;
item->requiredAnimState = BEAR_STROLL;
} else if( bear->mood == MOOD_BORED || GetRandomControl() < 0x50 ) {
item->requiredAnimState = BEAR_ROAR;
item->goalAnimState = BEAR_REAR;
} else if( info.distance > 0x400000 || GetRandomControl() < 0x600 ) {
item->requiredAnimState = BEAR_STOP;
item->goalAnimState = BEAR_REAR;
}
break;
case BEAR_ATTACK2 :
if( item->requiredAnimState == BEAR_STROLL && CHK_ANY(item->touchBits, BEAR_TOUCH) ) {
LaraItem->hitPoints -= 400;
LaraItem->hit_status = 1;
item->requiredAnimState = BEAR_REAR;
}
break;
case BEAR_ATTACK1 :
if( item->requiredAnimState == BEAR_STROLL && CHK_ANY(item->touchBits, BEAR_TOUCH) )
{
CreatureEffect(item, &BearBite, DoBloodSplat);
LaraItem->hitPoints -= 200;
LaraItem->hit_status = 1;
item->requiredAnimState = BEAR_STOP;
}
break;
default :
break;
}
}
CreatureHead(item, head);
CreatureAnimation(itemID, angle, 0);
}
/*
* NOTE: there is no inject function for tomb2gold code
*/
================================================
FILE: game/bear.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef BEAR_H_INCLUDED
#define BEAR_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// NOTE: this function is presented in the "Golden Mask" only
void __cdecl BearControl(__int16 itemID); // tomb2gold:0x0040C860
#endif // BEAR_H_INCLUDED
================================================
FILE: game/bird.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/bird.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Bird() {
// INJECT(0x0040C860, InitialiseEagle);
// INJECT(0x0040C8F0, EagleControl);
}
================================================
FILE: game/bird.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef BIRD_H_INCLUDED
#define BIRD_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define InitialiseEagle ((void(__cdecl*)(__int16)) 0x0040C860)
#define EagleControl ((void(__cdecl*)(__int16)) 0x0040C8F0)
#endif // BIRD_H_INCLUDED
================================================
FILE: game/boat.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/boat.h"
#include "3dsystem/phd_math.h"
#include "game/control.h"
#include "game/items.h"
#include "game/missile.h"
#include "specific/game.h"
#include "specific/output.h"
#include "global/vars.h"
void __cdecl DoWakeEffect(ITEM_INFO *item) {
__int16 frame_number, fxID;
int i;
FX_INFO *fx;
PhdMatrixPtr->_23 = 0;
S_CalculateLight(item->pos.x, item->pos.y, item->pos.z, item->roomNumber);
frame_number = Objects[ID_WATER_SPRITE].nMeshes * GetRandomDraw() >> 15;
for (i = 0; i < 3; ++i) {
fxID = CreateEffect(item->roomNumber);
if (fxID != -1) {
fx = &Effects[fxID];
fx->pos.x = item->pos.x + ((-700 * phd_sin(item->pos.rotY) + 300 * (i - 1) * phd_cos(item->pos.rotY)) >> W2V_SHIFT);
fx->pos.y = item->pos.y;
fx->pos.z = item->pos.z + ((-700 * phd_cos(item->pos.rotY) - 300 * (i - 1) * phd_sin(item->pos.rotY)) >> W2V_SHIFT);
fx->pos.rotY = PHD_90 * (i - 1) + item->pos.rotY;
fx->room_number = item->roomNumber;
fx->frame_number = frame_number;
fx->counter = 20;
fx->object_number = ID_WATER_SPRITE;
fx->speed = item->speed >> 2;
if (item->speed < 64) {
fx->fallspeed = (ABS(item->speed) - 64) * GetRandomDraw() >> 15;
} else {
fx->fallspeed = 0;
}
fx->shade = LsAdder - 768;
CLAMPL(fx->shade, 0);
}
}
}
void __cdecl GondolaControl(__int16 itemID) {
ITEM_INFO *item;
__int16 roomID;
item = &Items[itemID];
switch (item->currentAnimState) {
case 1:
if (item->goalAnimState == 2) {
item->meshBits = 0xFF;
ExplodingDeath(itemID, 0xF0, 0);
}
break;
case 3:
item->pos.y += 50;
roomID = item->roomNumber;
item->floor = GetHeight(GetFloor(item->pos.x, item->pos.y, item->pos.z, &roomID), item->pos.x, item->pos.y, item->pos.z);
if (item->pos.y >= item->floor) {
item->goalAnimState = 4;
item->pos.y = item->floor;
}
break;
}
AnimateItem(item);
if (item->status == ITEM_DISABLED)
RemoveActiveItem(itemID);
}
/*
* Inject function
*/
void Inject_Boat() {
// INJECT(0x0040CB10, InitialiseBoat);
// INJECT(0x0040CB50, BoatCheckGeton);
// INJECT(0x0040CCC0, BoatCollision);
// INJECT(0x0040CE20, TestWaterHeight);
// INJECT(0x0040CF20, DoBoatShift);
INJECT(0x0040D0F0, DoWakeEffect);
// INJECT(0x0040D270, DoBoatDynamics);
// INJECT(0x0040D2C0, BoatDynamics);
// INJECT(0x0040D7A0, BoatUserControl);
// INJECT(0x0040D930, BoatAnimation);
// INJECT(0x0040DAA0, BoatControl);
INJECT(0x0040E0D0, GondolaControl);
}
================================================
FILE: game/boat.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef BOAT_H_INCLUDED
#define BOAT_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x0040CB10: InitialiseBoat
// 0x0040CB50: BoatCheckGeton
// 0x0040CCC0: BoatCollision
// 0x0040CE20: TestWaterHeight
// 0x0040CF20: DoBoatShift
void __cdecl DoWakeEffect(ITEM_INFO *item); // 0x0040D0F0
// 0x0040D270: DoBoatDynamics
// 0x0040D2C0: BoatDynamics
// 0x0040D7A0: BoatUserControl
// 0x0040D930: BoatAnimation
// 0x0040DAA0: BoatControl
void __cdecl GondolaControl(__int16 itemID); // 0x0040E0D0
#endif // BOAT_H_INCLUDED
================================================
FILE: game/box.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/box.h"
#include "game/control.h"
#include "game/items.h"
#include "game/lot.h"
#include "game/missile.h"
#include "global/vars.h"
void __cdecl CreatureDie(__int16 itemID, BOOL explode) {
ITEM_INFO *item = &Items[itemID];
item->collidable = 0;
item->hitPoints = HP_DONT_TARGET;
if( explode ) {
ExplodingDeath(itemID, ~0, 0);
KillItem(itemID);
} else {
RemoveActiveItem(itemID);
}
DisableBaddieAI(itemID);
item->flags |= IFL_INVISIBLE;
if( item->clear_body ) {
item->nextActive = PrevItemActive;
PrevItemActive = itemID;
}
ITEM_INFO *pickup = NULL;
for( int i = item->carriedItem; i != -1; i = pickup->carriedItem ) {
pickup = &Items[i];
pickup->pos.x = item->pos.x;
pickup->pos.y = item->pos.y;
pickup->pos.z = item->pos.z;
ItemNewRoom(i, item->roomNumber);
}
}
void __cdecl CreatureKill(ITEM_INFO *item, int killAnim, int killState, int laraKillState) {
#ifdef FEATURE_CHEAT
// Return Lara to normal state if Dozy cheat enabled
if( Lara.water_status == LWS_Cheat ) {
Lara.water_status = LWS_AboveWater;
Lara.mesh_effects = 0;
}
#endif // FEATURE_CHEAT
item->animNumber = killAnim + Objects[item->objectID].animIndex;
item->frameNumber = Anims[item->animNumber].frameBase;
item->currentAnimState = killState;
LaraItem->animNumber = Objects[ID_LARA_EXTRA].animIndex;
LaraItem->frameNumber = Anims[LaraItem->animNumber].frameBase;
LaraItem->currentAnimState = 0;
LaraItem->goalAnimState = laraKillState;
LaraItem->pos.x = item->pos.x;
LaraItem->pos.y = item->pos.y;
LaraItem->pos.z = item->pos.z;
LaraItem->pos.rotY = item->pos.rotY;
LaraItem->pos.rotX = item->pos.rotX;
LaraItem->pos.rotZ = item->pos.rotZ;
LaraItem->fallSpeed = 0;
LaraItem->gravity = 0;
LaraItem->speed = 0;
if( LaraItem->roomNumber != item->roomNumber ) {
ItemNewRoom(Lara.item_number, item->roomNumber);
}
AnimateItem(LaraItem);
LaraItem->goalAnimState = laraKillState;
LaraItem->currentAnimState = laraKillState;
Lara.extra_anim = 1;
Lara.hit_direction = -1;
Lara.air = -1;
Lara.gun_status = LGS_HandBusy;
Lara.gun_type = LGT_Unarmed;
Camera.pos.roomNumber = LaraItem->roomNumber;
}
/*
* Inject function
*/
void Inject_Box() {
// INJECT(0x0040E190, InitialiseCreature);
// INJECT(0x0040E1C0, CreatureActive);
// INJECT(0x0040E210, CreatureAIInfo);
// INJECT(0x0040E470, SearchLOT);
// INJECT(0x0040E670, UpdateLOT);
// INJECT(0x0040E6E0, TargetBox);
// INJECT(0x0040E780, StalkBox);
// INJECT(0x0040E880, EscapeBox);
// INJECT(0x0040E930, ValidBox);
// INJECT(0x0040E9E0, CreatureMood);
// INJECT(0x0040EE50, CalculateTarget);
// INJECT(0x0040F2B0, CreatureCreature);
// INJECT(0x0040F3B0, BadFloor);
INJECT(0x0040F440, CreatureDie);
// INJECT(0x0040F500, CreatureAnimation);
// INJECT(0x0040FDD0, CreatureTurn);
// INJECT(0x0040FEB0, CreatureTilt);
// INJECT(0x0040FEF0, CreatureHead);
// INJECT(0x0040FF40, CreatureNeck);
// INJECT(0x0040FF90, CreatureFloat);
// INJECT(0x00410040, CreatureUnderwater);
// INJECT(0x00410090, CreatureEffect);
// INJECT(0x004100F0, CreatureVault);
INJECT(0x00410230, CreatureKill);
// INJECT(0x004103A0, GetBaddieTarget);
}
================================================
FILE: game/box.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef BOX_H_INCLUDED
#define BOX_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define InitialiseCreature ((void(__cdecl*)(__int16)) 0x0040E190)
// 0x0040E1C0: CreatureActive
#define CreatureAIInfo ((void(__cdecl*)(ITEM_INFO *, AI_INFO *)) 0x0040E210)
// 0x0040E470: SearchLOT
// 0x0040E670: UpdateLOT
// 0x0040E6E0: TargetBox
// 0x0040E780: StalkBox
// 0x0040E880: EscapeBox
// 0x0040E930: ValidBox
#define CreatureMood ((void(__cdecl*)(ITEM_INFO *, AI_INFO *, BOOL)) 0x0040E9E0)
// 0x0040EE50: CalculateTarget
// 0x0040F2B0: CreatureCreature
// 0x0040F3B0: BadFloor
void __cdecl CreatureDie(__int16 itemID, BOOL explode); // 0x0040F440
#define CreatureAnimation ((int(__cdecl*)(__int16, __int16, __int16)) 0x0040F500)
#define CreatureTurn ((__int16(__cdecl*)(ITEM_INFO *, __int16)) 0x0040FDD0)
#define CreatureTilt ((void(__cdecl*)(ITEM_INFO *, __int16)) 0x0040FEB0)
#define CreatureHead ((void(__cdecl*)(ITEM_INFO *, __int16)) 0x0040FEF0)
// 0x0040FF40: CreatureNeck
// 0x0040FF90: CreatureFloat
// 0x00410040: CreatureUnderwater
#define CreatureEffect ((__int16(__cdecl*)(ITEM_INFO *, const BITE_INFO *, __int16(__cdecl*)(int, int, int, __int16, __int16, __int16))) 0x00410090)
// 0x004100F0: CreatureVault
void __cdecl CreatureKill(ITEM_INFO *item, int killAnim, int killState, int laraKillState); // 0x00410230
// 0x004103A0: GetBaddieTarget
#endif // BOX_H_INCLUDED
================================================
FILE: game/camera.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/camera.h"
#include "3dsystem/3d_gen.h"
#include "3dsystem/phd_math.h"
#include "game/cinema.h"
#include "game/control.h"
#include "game/draw.h"
#include "game/sound.h"
#include "specific/game.h"
#include "specific/sndpc.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/joy_output.h"
#endif // FEATURE_INPUT_IMPROVED
#define CAM_DISTANCE (0x600)
void __cdecl InitialiseCamera() {
Camera.shift = LaraItem->pos.y - 0x400;
Camera.target.x = LaraItem->pos.x;
Camera.target.y = Camera.shift;
Camera.target.z = LaraItem->pos.z;
Camera.target.roomNumber = LaraItem->roomNumber;
Camera.pos.x = Camera.target.x;
Camera.pos.y = Camera.target.y;
Camera.pos.z = Camera.target.z - 100;
Camera.pos.roomNumber = Camera.target.roomNumber;
Camera.targetDistance = CAM_DISTANCE;
Camera.item = NULL;
Camera.numberFrames = 1;
if ( !(Lara.extra_anim) ) {
Camera.type = CAM_Chase;
}
Camera.speed = 1;
Camera.flags = 0;
Camera.bounce = 0;
Camera.number = -1;
Camera.fixedCamera = 0;
AlterFOV(80 * PHD_DEGREE);
CalculateCamera();
}
void __cdecl MoveCamera(GAME_VECTOR *destination, int speed) {
Camera.pos.x += (destination->x - Camera.pos.x) / speed;
Camera.pos.z += (destination->z - Camera.pos.z) / speed;
Camera.pos.y += (destination->y - Camera.pos.y) / speed;
Camera.pos.roomNumber = destination->roomNumber;
IsChunkyCamera = FALSE;
FLOOR_INFO *floor = GetFloor(Camera.pos.x, Camera.pos.y, Camera.pos.z, &Camera.pos.roomNumber);
int height = GetHeight(floor, Camera.pos.x, Camera.pos.y, Camera.pos.z) - 0x100;
if( Camera.pos.y >= height && destination->y >= height ) {
LOS(&Camera.target, &Camera.pos);
floor = GetFloor(Camera.pos.x, Camera.pos.y, Camera.pos.z, &Camera.pos.roomNumber);
height = GetHeight(floor, Camera.pos.x, Camera.pos.y, Camera.pos.z) - 0x100;
}
int ceiling = GetCeiling(floor, Camera.pos.x, Camera.pos.y, Camera.pos.z) + 0x100;
if( height < ceiling ) {
height = ceiling = (height + ceiling) / 2;
}
if( Camera.bounce ) {
int shake;
#ifdef FEATURE_INPUT_IMPROVED
shake = 0xFFFF*(UINT64)(ABS(Camera.bounce)+50)/250;
JoyRumble(shake, shake, 6, shake/6, 12, true);
#endif // FEATURE_INPUT_IMPROVED
if( Camera.bounce > 0 ) {
Camera.pos.y += Camera.bounce;
Camera.target.y += Camera.bounce;
Camera.bounce = 0;
} else {
shake = Camera.bounce * (GetRandomControl() - 0x4000) / 0x7FFF;
Camera.pos.x += shake;
Camera.target.y += shake;
shake = Camera.bounce * (GetRandomControl() - 0x4000) / 0x7FFF;
Camera.pos.y += shake;
Camera.target.y += shake;
shake = Camera.bounce * (GetRandomControl() - 0x4000);
Camera.pos.z += shake / 0x7FFF;
Camera.target.z += shake / 0x7FFF;
Camera.bounce += 5;
}
}
if( Camera.pos.y <= height ) {
if ( Camera.pos.y >= ceiling ) {
Camera.shift = 0;
} else {
Camera.shift = ceiling - Camera.pos.y;
}
} else {
Camera.shift = height - Camera.pos.y;
}
GetFloor(Camera.pos.x, Camera.shift + Camera.pos.y, Camera.pos.z, &Camera.pos.roomNumber);
phd_LookAt(Camera.pos.x, Camera.pos.y + Camera.shift, Camera.pos.z,
Camera.target.x, Camera.target.y, Camera.target.z, 0);
if( Camera.isLaraMic ) {
Camera.actualAngle = Lara.torso_y_rot + Lara.head_y_rot + LaraItem->pos.rotY;
Camera.micPos.x = LaraItem->pos.x;
Camera.micPos.y = LaraItem->pos.y;
Camera.micPos.z = LaraItem->pos.z;
} else {
Camera.actualAngle = phd_atan(Camera.target.z - Camera.pos.z, Camera.target.x - Camera.pos.x);
Camera.micPos.x = Camera.pos.x + ((phd_sin(Camera.actualAngle) * PhdPersp) >> W2V_SHIFT);
Camera.micPos.z = Camera.pos.z + ((phd_cos(Camera.actualAngle) * PhdPersp) >> W2V_SHIFT);
Camera.micPos.y = Camera.pos.y;
}
}
void __cdecl LookCamera(ITEM_INFO *item) {
int zOld = Camera.target.z;
int xOld = Camera.target.x;
Camera.target.z = item->pos.z;
Camera.target.x = item->pos.x;
Camera.targetAngle = item->pos.rotY + Lara.torso_y_rot + Lara.head_y_rot;
Camera.targetDistance = CAM_DISTANCE;
Camera.targetElevation = item->pos.rotX + Lara.torso_x_rot + Lara.head_x_rot;
Camera.shift = (-0x200 * phd_sin(Camera.targetElevation)) >> W2V_SHIFT;
Camera.target.z += Camera.shift * phd_cos(item->pos.rotY) >> W2V_SHIFT;
Camera.target.x += Camera.shift * phd_sin(item->pos.rotY) >> W2V_SHIFT;
if( !GoodPosition(Camera.target.x, Camera.target.y, Camera.target.z, Camera.target.roomNumber) ) {
Camera.target.x = item->pos.x;
Camera.target.z = item->pos.z;
}
Camera.target.y += ShiftClamp(&Camera.target, 306);
GAME_VECTOR goal;
int dist = Camera.targetDistance * phd_cos(Camera.targetElevation) >> W2V_SHIFT;
goal.x = Camera.target.x - (dist * phd_sin(Camera.targetAngle) >> W2V_SHIFT);
goal.y = Camera.target.y + (Camera.targetDistance * phd_sin(Camera.targetElevation) >> W2V_SHIFT);
goal.z = Camera.target.z - (dist * phd_cos(Camera.targetAngle) >> W2V_SHIFT);
goal.roomNumber = Camera.pos.roomNumber;
SmartShift(&goal, ClipCamera);
Camera.target.z = zOld + (Camera.target.z - zOld) / Camera.speed;
Camera.target.x = xOld + (Camera.target.x - xOld) / Camera.speed;
MoveCamera(&goal, Camera.speed);
}
void __cdecl FixedCamera() {
GAME_VECTOR goal;
OBJECT_VECTOR *fixed = &Camera.fixed[Camera.number];
goal.x = fixed->x;
goal.y = fixed->y;
goal.z = fixed->z;
goal.roomNumber = fixed->data;
if( !LOS(&Camera.target, &goal) ) {
ShiftClamp(&goal, 0x100);
}
Camera.fixedCamera = 1;
MoveCamera(&goal, Camera.speed);
if( Camera.timer && !--Camera.timer ) {
Camera.timer = -1;
}
}
void __cdecl CalculateCamera() {
ITEM_INFO *item;
__int16 *bounds;
FLOOR_INFO *floor;
int y, shift, fixedCamera;
__int16 angle, tilt, rot;
if( CHK_ANY(RoomInfo[Camera.pos.roomNumber].flags, ROOM_UNDERWATER) ) {
PlaySoundEffect(60, 0, SFX_ALWAYS);
if( !Camera.underwater ) {
#ifdef FEATURE_AUDIO_IMPROVED
extern double UnderwaterMusicMute;
double volume = (1.0 - UnderwaterMusicMute) * (double)(MusicVolume * 25 + 5);
if( MusicVolume > 0 && volume >= 1.0 ) {
S_CDVolume((DWORD)volume);
} else {
S_CDVolume(0);
}
#else // FEATURE_AUDIO_IMPROVED
S_CDVolume(0); // NOTE: Core supposed to pause CD Audio this way
#endif // FEATURE_AUDIO_IMPROVED
Camera.underwater = 1;
}
} else if ( Camera.underwater ) {
if( MusicVolume ) {
S_CDVolume(MusicVolume * 25 + 5);
}
Camera.underwater = 0;
}
if( Camera.type == CAM_Cinematic ) {
InGameCinematicCamera();
return;
}
if( Camera.flags != CFL_NoChunky ) {
IsChunkyCamera = 1;
}
fixedCamera = (Camera.item && (Camera.type == CAM_Fixed || Camera.type == CAM_Heavy)) ? 1 : 0;
item = fixedCamera ? Camera.item : LaraItem;
bounds = GetBoundsAccurate(item);
y = item->pos.y;
if( fixedCamera ) {
y += (bounds[2] + bounds[3]) / 2;
} else {
y += bounds[3] + (bounds[2] - bounds[3]) * 3 / 4;
}
if( Camera.item && !fixedCamera ) {
shift = phd_sqrt(SQR(Camera.item->pos.x - item->pos.x) + SQR(Camera.item->pos.z - item->pos.z));
angle = phd_atan(Camera.item->pos.z - item->pos.z, Camera.item->pos.x - item->pos.x) - item->pos.rotY;
bounds = GetBoundsAccurate(Camera.item);
tilt = phd_atan(shift, y - Camera.item->pos.y - (bounds[2] + bounds[3]) / 2);
angle /= 2;
tilt /= 2;
if( angle > -50*PHD_DEGREE && angle < 50*PHD_DEGREE && tilt > -85*PHD_DEGREE && tilt < 85*PHD_DEGREE ) {
rot = angle - Lara.head_y_rot;
CLAMP(rot, -4*PHD_DEGREE, 4*PHD_DEGREE);
Lara.head_y_rot += rot;
Lara.torso_y_rot += rot;
rot = tilt - Lara.head_x_rot;
CLAMP(rot, -4*PHD_DEGREE, 4*PHD_DEGREE);
Lara.head_x_rot += rot;
Lara.torso_x_rot += rot;
Camera.type = CAM_Look;
Camera.item->looked_at = 1;
}
}
if( Camera.type == CAM_Look || Camera.type == CAM_Combat ) {
y -= 0x100;
Camera.target.roomNumber = item->roomNumber;
if( Camera.fixedCamera ) {
Camera.target.y = y;
Camera.speed = 1;
} else {
Camera.target.y += (y - Camera.target.y) / 4;
Camera.speed = (Camera.type != CAM_Look) ? 8 : 4;
}
Camera.fixedCamera = 0;
if ( Camera.type == CAM_Look ) {
LookCamera(item);
} else {
CombatCamera(item);
}
} else {
Camera.target.x = item->pos.x;
Camera.target.z = item->pos.z;
if( Camera.flags == CFL_FollowCenter ) {
shift = (bounds[4] + bounds[5]) / 2;
Camera.target.z += shift * phd_cos(item->pos.rotY) >> W2V_SHIFT;
Camera.target.x += shift * phd_sin(item->pos.rotY) >> W2V_SHIFT;
}
Camera.target.roomNumber = item->roomNumber;
if ( Camera.fixedCamera == fixedCamera ) {
Camera.fixedCamera = 0;
Camera.target.y += (y - Camera.target.y) / 4;
} else {
Camera.target.y = y;
Camera.fixedCamera = 1;
Camera.speed = 1;
}
floor = GetFloor(Camera.target.x, Camera.target.y, Camera.target.z, &Camera.target.roomNumber);
if( Camera.target.y > GetHeight(floor, Camera.target.x, Camera.target.y, Camera.target.z) ) {
IsChunkyCamera = 0;
}
if( Camera.type == CAM_Chase || Camera.flags == CFL_ChaseObject ) {
ChaseCamera(item);
} else {
FixedCamera();
}
}
Camera.last = Camera.number;
Camera.fixedCamera = fixedCamera;
if( Camera.type != CAM_Heavy || Camera.timer == -1 ) {
Camera.type = CAM_Chase;
Camera.speed = 10;
Camera.number = -1;
Camera.last_item = Camera.item;
Camera.item = NULL;
Camera.targetElevation = 0;
Camera.targetAngle = 0;
Camera.targetDistance = CAM_DISTANCE;
Camera.flags = CFL_None;
}
IsChunkyCamera = 0;
}
/*
* Inject function
*/
void Inject_Camera() {
INJECT(0x00410580, InitialiseCamera);
INJECT(0x00410630, MoveCamera);
// INJECT(0x004109B0, ClipCamera);
// INJECT(0x00410A90, ShiftCamera);
// INJECT(0x00410BF0, BadPosition);
// INJECT(0x00410C40, SmartShift);
// INJECT(0x004113D0, ChaseCamera);
// INJECT(0x004114C0, ShiftClamp);
// INJECT(0x00411660, CombatCamera);
INJECT(0x004117F0, LookCamera);
INJECT(0x004119E0, FixedCamera);
INJECT(0x00411A80, CalculateCamera);
}
================================================
FILE: game/camera.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef CAMERA_H_INCLUDED
#define CAMERA_H_INCLUDED
#include "global/types.h"
typedef void(__cdecl *CB_SMARTCAM)(int*, int*, int*, int, int, int, int, int, int, int);
/*
* Function list
*/
void __cdecl InitialiseCamera(); // 0x00410580
void __cdecl MoveCamera(GAME_VECTOR *destination, int speed); // 0x00410630
#define ClipCamera ((void(__cdecl*)(int*, int*, int*, int, int, int, int, int, int, int)) 0x004109B0)
#define ShiftCamera ((void(__cdecl*)(int*, int*, int*, int, int, int, int, int, int, int)) 0x00410A90)
#define GoodPosition ((FLOOR_INFO*(__cdecl*)(int, int, int, __int16)) 0x00410BF0)
#define SmartShift ((void(__cdecl*)(GAME_VECTOR*, CB_SMARTCAM)) 0x00410C40)
#define ChaseCamera ((void(__cdecl*)(ITEM_INFO*)) 0x004113D0)
#define ShiftClamp ((int(__cdecl*)(GAME_VECTOR*, int)) 0x004114C0)
#define CombatCamera ((void(__cdecl*)(ITEM_INFO*)) 0x00411660)
void __cdecl LookCamera(ITEM_INFO *item); // 0x004117F0
void __cdecl FixedCamera(); // 0x004119E0
void __cdecl CalculateCamera(); // 0x00411A80
#endif // CAMERA_H_INCLUDED
================================================
FILE: game/cinema.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/cinema.h"
#include "game/draw.h"
#include "game/hair.h"
#include "game/setup.h"
#include "specific/frontend.h"
#include "specific/input.h"
#include "specific/output.h"
#include "specific/sndpc.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/joy_output.h"
#endif // FEATURE_INPUT_IMPROVED
void __cdecl SetCutsceneTrack(int track) {
CineTrackID = track;
}
int __cdecl StartCinematic(int levelID) {
int result;
BOOL soundWasActive;
int nTicks;
CineLevelID = levelID;
IsTitleLoaded = FALSE;
S_FadeToBlack();
if( !InitialiseLevel(levelID, GFL_CUTSCENE) ) {
return 2;
}
InitCinematicRooms();
InitialisePlayer1(Lara.item_number);
Camera.targetAngle = CineTargetAngle;
soundWasActive = SoundIsActive;
SoundIsActive = FALSE;
CineFrameIdx = 0;
S_ClearScreen();
if( !StartSyncedAudio(CineTrackID) ) {
return 1;
}
S_CDVolume(255);
CineCurrentFrame = 0;
do {
DrawPhaseCinematic();
nTicks = CineCurrentFrame - TICKS_PER_FRAME * (CineFrameIdx - 4);
if( nTicks < TICKS_PER_FRAME ) {
nTicks = TICKS_PER_FRAME;
}
result = DoCinematic(nTicks);
} while( !result );
S_CDVolume((MusicVolume > 0) ? (25 * MusicVolume + 5) : 0);
S_CDStop();
SoundIsActive = soundWasActive;
S_SoundStopAllSamples();
IsLevelComplete = TRUE;
return result;
}
void __cdecl InitCinematicRooms() {
for( int i=0; i= 0 ) {
RoomInfo[RoomInfo[i].flippedRoom].boundActive = 1;
}
RoomInfo[i].flags |= ROOM_OUTSIDE;
}
DrawRoomsCount = 0;
for( int i=0; i= 0; tickCount -= PHD_ONE ) {
if( S_UpdateInput() ) {
return 3;
}
if( CHK_ANY(InputStatus, IN_ACTION) ) {
return 1;
}
if( CHK_ANY(InputStatus, IN_OPTION) ) {
return 2;
}
DynamicLightCount = 0;
for( id = NextItemActive; id >= 0; id = next ) {
next = Items[id].nextActive;
if( Objects[Items[id].objectID].control ) {
Objects[Items[id].objectID].control(id);
}
}
for( id = NextEffectActive; id >= 0; id = next ) {
next = Effects[id].next_active;
if( Objects[Effects[id].object_number].control ) {
Objects[Effects[id].object_number].control(id);
}
}
HairControl(1);
CalculateCinematicCamera();
if( ++CineFrameIdx >= CineFramesCount ) {
return 1;
}
}
CineCurrentFrame = S_CDGetLoc()*4/5;
#ifdef FEATURE_INPUT_IMPROVED
UpdateJoyOutput(false);
#endif // FEATURE_INPUT_IMPROVED
return 0;
}
/*
* Inject function
*/
void Inject_Cinema() {
INJECT(0x00411F30, SetCutsceneTrack);
INJECT(0x00411F40, StartCinematic);
INJECT(0x00412060, InitCinematicRooms);
INJECT(0x00412100, DoCinematic);
// INJECT(0x00412270, CalculateCinematicCamera);
// INJECT(0x004123B0, GetCinematicRoom);
// INJECT(0x00412430, ControlCinematicPlayer);
// INJECT(0x00412510, LaraControlCinematic);
// INJECT(0x004125B0, InitialisePlayer1);
// INJECT(0x00412640, InitialiseGenPlayer);
// INJECT(0x00412680, InGameCinematicCamera);
}
================================================
FILE: game/cinema.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef CINEMA_H_INCLUDED
#define CINEMA_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl SetCutsceneTrack(int track); // 0x00411F30
int __cdecl StartCinematic(int levelID); // 0x00411F40
void __cdecl InitCinematicRooms(); // 0x00412060
int __cdecl DoCinematic(int nTicks); // 0x00412100
#define CalculateCinematicCamera ((void(__cdecl*)(void)) 0x00412270)
#define GetCinematicRoom ((int(__cdecl*)(int, int, int)) 0x004123B0)
#define ControlCinematicPlayer ((void(__cdecl*)(__int16)) 0x00412430)
#define LaraControlCinematic ((void(__cdecl*)(__int16)) 0x00412510)
#define InitialisePlayer1 ((void(__cdecl*)(__int16)) 0x004125B0)
#define InitialiseGenPlayer ((void(__cdecl*)(__int16)) 0x00412640)
#define InGameCinematicCamera ((void(__cdecl*)(void)) 0x00412680)
#endif // CINEMA_H_INCLUDED
================================================
FILE: game/collide.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/collide.h"
#include "game/control.h"
#include "global/vars.h"
int __cdecl CollideStaticObjects(COLL_INFO *coll, int x, int y, int z, __int16 roomID, int hite) {
int rxMin = x - coll->radius;
int rxMax = x + coll->radius;
int ryMin = y - hite;
int ryMax = y;
int rzMin = z - coll->radius;
int rzMax = z + coll->radius;
coll->hitStatic = 0;
GetNearByRooms(x, y, z, coll->radius + 50, hite + 50, roomID);
// outer loop
for( int i = 0; i < DrawRoomsCount; ++i ) {
ROOM_INFO *room = &RoomInfo[DrawRoomsArray[i]];
for( int j = 0; j < room->numMeshes; ++j ) {
MESH_INFO *mesh = &room->mesh[j];
if( CHK_ANY(StaticObjects[mesh->staticNumber].flags, 1) ) {
continue;
}
STATIC_BOUNDS *bounds = &StaticObjects[mesh->staticNumber].collisionBounds;
int yMin = mesh->y + bounds->yMin;
int yMax = mesh->y + bounds->yMax;
int xMin = mesh->x;
int xMax = mesh->x;
int zMin = mesh->z;
int zMax = mesh->z;
switch( mesh->yRot ) {
case -PHD_90: // west
xMin -= bounds->zMax;
xMax -= bounds->zMin;
zMin += bounds->xMin;
zMax += bounds->xMax;
break;
case -PHD_180: // south
xMin -= bounds->xMax;
xMax -= bounds->xMin;
zMin -= bounds->zMax;
zMax -= bounds->zMin;
break;
case PHD_90: // east
xMin += bounds->zMin;
xMax += bounds->zMax;
zMin -= bounds->xMax;
zMax -= bounds->xMin;
break;
default: // north
xMin += bounds->xMin;
xMax += bounds->xMax;
zMin += bounds->zMin;
zMax += bounds->zMax;
break;
}
if( rxMax <= xMin || rxMin >= xMax ||
ryMax <= yMin || ryMin >= yMax ||
rzMax <= zMin || rzMin >= zMax )
{
continue;
}
int shift[2];
shift[0] = rxMax - xMin;
shift[1] = xMax - rxMin;
int xShift = (shift[0] < shift[1]) ? -shift[0] : shift[1];
shift[0] = rzMax - zMin;
shift[1] = zMax - rzMin;
int zShift = (shift[0] < shift[1]) ? -shift[0] : shift[1];
switch ( coll->quadrant ) {
case 0: // north
if( xShift > coll->radius || xShift < -coll->radius ) {
coll->shift.x = coll->old.x - x;
coll->shift.z = zShift;
coll->collType = COLL_FRONT;
} else if( xShift > 0 ) {
coll->shift.x = xShift;
coll->shift.z = 0;
coll->collType = COLL_LEFT;
} else if( xShift < 0 ) {
coll->shift.x = xShift;
coll->shift.z = 0;
coll->collType = COLL_RIGHT;
}
break;
case 1: // east
if( zShift > coll->radius || zShift < -coll->radius ) {
coll->shift.x = xShift;
coll->shift.z = coll->old.z - z;
coll->collType = COLL_FRONT;
} else if( zShift > 0 ) {
coll->shift.x = 0;
coll->shift.z = zShift;
coll->collType = COLL_RIGHT;
} else if( zShift < 0 ) {
coll->shift.x = 0;
coll->shift.z = zShift;
coll->collType = COLL_LEFT;
}
break;
case 2: // south
if( xShift > coll->radius || xShift < -coll->radius ) {
coll->shift.x = coll->old.x - x;
coll->shift.z = zShift;
coll->collType = COLL_FRONT;
} else if( xShift > 0 ) {
coll->shift.x = xShift;
coll->shift.z = 0;
coll->collType = COLL_RIGHT;
} else if( xShift < 0 ) {
coll->shift.x = xShift;
coll->shift.z = 0;
coll->collType = COLL_LEFT;
}
break;
case 3: // west
if( zShift > coll->radius || zShift < -coll->radius ) {
coll->shift.x = xShift;
coll->shift.z = coll->old.z - z;
coll->collType = COLL_FRONT;
} else if( zShift > 0 ) {
coll->shift.x = 0;
coll->shift.z = zShift;
coll->collType = COLL_LEFT;
} else if( zShift < 0 ) {
coll->shift.x = 0;
coll->shift.z = zShift;
coll->collType = COLL_RIGHT;
}
break;
}
coll->hitStatic = 1;
return 1;
}
}
return 0;
}
void __cdecl GetNearByRooms(int x, int y, int z, int r, int h, __int16 roomID) {
DrawRoomsArray[0] = roomID;
DrawRoomsCount = 1;
GetNewRoom(x + r, y, z + r, roomID);
GetNewRoom(x - r, y, z + r, roomID);
GetNewRoom(x + r, y, z - r, roomID);
GetNewRoom(x - r, y, z - r, roomID);
GetNewRoom(x + r, y - h, z + r, roomID);
GetNewRoom(x - r, y - h, z + r, roomID);
GetNewRoom(x + r, y - h, z - r, roomID);
GetNewRoom(x - r, y - h, z - r, roomID);
}
void __cdecl GetNewRoom(int x, int y, int z, __int16 roomID) {
GetFloor(x, y, z, &roomID);
for( int i = 0; i < DrawRoomsCount; ++i ) {
if( DrawRoomsArray[i] == roomID ) {
return;
}
}
DrawRoomsArray[DrawRoomsCount++] = roomID;
}
/*
* Inject function
*/
void Inject_Collide() {
// INJECT(0x004128D0, GetCollisionInfo);
// INJECT(0x00412F90, FindGridShift);
INJECT(0x00412FC0, CollideStaticObjects);
INJECT(0x004133B0, GetNearByRooms);
INJECT(0x00413480, GetNewRoom);
// INJECT(0x004134E0, ShiftItem);
// INJECT(0x00413520, UpdateLaraRoom);
// INJECT(0x00413580, GetTiltType);
// INJECT(0x00413620, LaraBaddieCollision);
// INJECT(0x004137C0, EffectSpaz);
// INJECT(0x00413840, CreatureCollision);
// INJECT(0x004138C0, ObjectCollision);
// INJECT(0x00413920, DoorCollision);
// INJECT(0x004139A0, TrapCollision);
// INJECT(0x00413A10, ItemPushLara);
// INJECT(0x00413D20, TestBoundsCollide);
// INJECT(0x00413DF0, TestLaraPosition);
// INJECT(0x00413F30, AlignLaraPosition);
// INJECT(0x00414070, MoveLaraPosition);
// INJECT(0x00414200, Move3DPosTo3DPos);
}
================================================
FILE: game/collide.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef COLLIDE_H_INCLUDED
#define COLLIDE_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x004128D0: GetCollisionInfo
// 0x00412F90: FindGridShift
int __cdecl CollideStaticObjects(COLL_INFO *coll, int x, int y, int z, __int16 roomID, int hite); // 0x00412FC0
void __cdecl GetNearByRooms(int x, int y, int z, int r, int h, __int16 roomID); // 0x004133B0
void __cdecl GetNewRoom(int x, int y, int z, __int16 roomID); // 0x00413480
// 0x004134E0: ShiftItem
#define UpdateLaraRoom ((void(__cdecl*)(ITEM_INFO*, int)) 0x00413520)
// 0x00413580: GetTiltType
// 0x00413620: LaraBaddieCollision
// 0x004137C0: EffectSpaz
#define CreatureCollision ((void(__cdecl*)(__int16, ITEM_INFO *, COLL_INFO *)) 0x00413840)
#define ObjectCollision ((void(__cdecl*)(__int16, ITEM_INFO *, COLL_INFO *)) 0x004138C0)
// 0x00413920: DoorCollision
// 0x004139A0: TrapCollision
// 0x00413A10: ItemPushLara
// 0x00413D20: TestBoundsCollide
// 0x00413DF0: TestLaraPosition
// 0x00413F30: AlignLaraPosition
// 0x00414070: MoveLaraPosition
// 0x00414200: Move3DPosTo3DPos
#endif // COLLIDE_H_INCLUDED
================================================
FILE: game/control.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/control.h"
#include "game/camera.h"
#include "game/demo.h"
#include "game/effects.h"
#include "game/hair.h"
#include "game/inventory.h"
#include "game/laramisc.h"
#include "game/savegame.h"
#include "specific/game.h"
#include "specific/input.h"
#include "specific/smain.h"
#include "specific/sndpc.h"
#include "global/vars.h"
#ifdef FEATURE_BACKGROUND_IMPROVED
#include "modding/pause.h"
#endif // FEATURE_BACKGROUND_IMPROVED
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/joy_output.h"
#endif // FEATURE_INPUT_IMPROVED
int __cdecl ControlPhase(int nTicks, BOOL demoMode) {
static int tickCount = 0;
int id = -1;
int next = -1;
int result = 0;
CLAMPG(nTicks, 5 * TICKS_PER_FRAME);
for( tickCount += nTicks; tickCount > 0; tickCount -= TICKS_PER_FRAME ) {
if( CD_TrackID > 0 ) {
S_CDLoop();
}
if( !CHK_ANY(GF_GameFlow.flags, GFF_CheatModeCheckDisabled) ) {
CheckCheatMode();
}
if( IsLevelComplete ) {
return 1;
}
S_UpdateInput();
if( IsResetFlag ) {
return GF_EXIT_TO_TITLE;
}
if( demoMode ) {
if( InputStatus ) {
return GF_GameFlow.onDemo_Interrupt;
}
GetDemoInput();
if( InputStatus == (DWORD)~0 ) {
InputStatus = 0;
return GF_GameFlow.onDemo_End;
}
} else {
if( CHK_ANY(GF_GameFlow.flags, GFF_NoInputTimeout) ) {
if( InputStatus ) {
NoInputCounter = 0;
} else if( ++NoInputCounter > GF_GameFlow.noInput_Time ) {
return GF_START_DEMO;
}
}
}
if( OverlayStatus == 2 || Lara.death_count > 10*30 || (Lara.death_count > 2*30 && InputStatus) ) {
if( demoMode ) {
return GF_GameFlow.onDeath_DemoMode;
}
if( CurrentLevel == 0 ) { // Lara's Home
return GF_EXIT_TO_TITLE;
}
if( OverlayStatus == 2 ) {
OverlayStatus = 1;
result = Display_Inventory(INV_DeathMode);
if( result ) {
return result;
}
} else {
OverlayStatus = 2;
}
}
if( !Lara.death_count && !Lara.extra_anim && (CHK_ANY(InputStatus, IN_OPTION|IN_LOAD|IN_SAVE) || OverlayStatus <= 0) ) {
if( OverlayStatus > 0 ) {
if( CHK_ANY(GF_GameFlow.flags, GFF_LoadSaveDisabled) ) {
OverlayStatus = 0;
} else if( CHK_ANY(InputStatus, IN_LOAD) ) {
OverlayStatus = -1;
} else if( CHK_ANY(InputStatus, IN_SAVE) ) {
OverlayStatus = -2;
} else {
OverlayStatus = 0;
}
} else {
if( OverlayStatus == -1 ) {
result = Display_Inventory(INV_LoadMode);
} else if( OverlayStatus == -2 ) {
result = Display_Inventory(INV_SaveMode);
} else {
result = Display_Inventory(INV_GameMode);
}
OverlayStatus = 1;
if( result ) {
if( InventoryExtraData[0] != 1 ) {
return result;
}
if( CurrentLevel == 0 ) { // Lara's Home
return 1;
}
CreateSaveGameInfo();
S_SaveGame(&SaveGame, sizeof(SaveGame), InventoryExtraData[1]);
S_SaveSettings();
}
}
}
#ifdef FEATURE_BACKGROUND_IMPROVED
if( !Lara.death_count && !Lara.extra_anim && CHK_ANY(InputStatus, IN_PAUSE) && S_Pause() ) {
return 1;
}
#endif // FEATURE_BACKGROUND_IMPROVED
DynamicLightCount = 0;
for( id = NextItemActive; id >= 0; id = next ) {
next = Items[id].nextActive;
// NOTE: there is no IFL_CLEARBODY check in the original code
if( Objects[Items[id].objectID].control && !CHK_ANY(Items[id].flags, IFL_CLEARBODY) ) {
Objects[Items[id].objectID].control(id);
}
}
for( id = NextEffectActive; id >= 0; id = next ) {
next = Effects[id].next_active;
if( Objects[Effects[id].object_number].control ) {
Objects[Effects[id].object_number].control(id);
}
}
LaraControl(0);
HairControl(0);
CalculateCamera();
SoundEffects();
--HealthBarTimer;
// Update statistics timer for normal levels
if( CurrentLevel != 0 || IsAssaultTimerActive ) {
++SaveGame.statistics.timer;
}
}
#ifdef FEATURE_INPUT_IMPROVED
UpdateJoyOutput(!IsDemoLevelType);
#endif // FEATURE_INPUT_IMPROVED
return 0;
}
void __cdecl TriggerCDTrack(__int16 value, UINT16 flags, __int16 type) {
if( value > 1 && value < 64 ) {
TriggerNormalCDTrack(value, flags, type);
}
}
void __cdecl TriggerNormalCDTrack(__int16 value, UINT16 flags, __int16 type) {
if( type != 2 ) {
UINT16 codebits = flags & IFL_CODEBITS;
if( CHK_ANY(codebits, CD_Flags[value]) ){
return;
}
if( CHK_ANY(flags, IFL_INVISIBLE) ) {
CD_Flags[value] |= codebits;
}
}
if( value == CD_TrackID ) {
UINT8 timer = CD_Flags[value] & 0xFF;
if( timer ) {
if( !--timer ) {
CD_TrackID = -1;
S_CDPlay(value, FALSE);
}
CD_Flags[value] = (CD_Flags[value] & ~0xFF) | timer;
}
} else {
UINT8 timer = flags & 0xFF;
if( timer ) {
CD_TrackID = value;
CD_Flags[value] = (CD_Flags[value] & ~0xFF) | ((timer * 30) & 0xFF);
} else {
S_CDPlay(value, FALSE);
}
}
}
/*
* Inject function
*/
void Inject_Control() {
INJECT(0x00414370, ControlPhase);
// INJECT(0x004146C0, AnimateItem);
// INJECT(0x00414A30, GetChange);
// INJECT(0x00414AE0, TranslateItem);
// INJECT(0x00414B40, GetFloor);
// INJECT(0x00414CE0, GetWaterHeight);
// INJECT(0x00414E50, GetHeight);
// INJECT(0x004150D0, RefreshCamera);
// INJECT(0x004151C0, TestTriggers);
// INJECT(0x004158A0, TriggerActive);
// INJECT(0x00415900, GetCeiling);
// INJECT(0x00415B60, GetDoor);
// INJECT(0x00415BB0, LOS);
// INJECT(0x00415C50, zLOS);
// INJECT(0x00415F40, xLOS);
// INJECT(0x00416230, ClipTarget);
// INJECT(0x00416310, ObjectOnLOS);
// INJECT(0x00416610, FlipMap);
// INJECT(0x004166D0, RemoveRoomFlipItems);
// INJECT(0x00416770, AddRoomFlipItems);
INJECT(0x004167D0, TriggerCDTrack);
INJECT(0x00416800, TriggerNormalCDTrack);
}
================================================
FILE: game/control.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef CONTROL_H_INCLUDED
#define CONTROL_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
int __cdecl ControlPhase(int nTicks, BOOL demoMode);
#define AnimateItem ((void(__cdecl*)(ITEM_INFO*)) 0x004146C0)
// 0x00414A30: GetChange
// 0x00414AE0: TranslateItem
#define GetFloor ((FLOOR_INFO*(__cdecl*)(int, int, int, __int16*)) 0x00414B40)
#define GetWaterHeight ((int(__cdecl*)(int, int, int, __int16)) 0x00414CE0)
#define GetHeight ((int(__cdecl*)(FLOOR_INFO*, int, int, int)) 0x00414E50)
// 0x004150D0: RefreshCamera
#define TestTriggers ((void(__cdecl*)(__int16*, BOOL)) 0x004151C0)
#define TriggerActive ((int(__cdecl*)(ITEM_INFO*)) 0x004158A0)
#define GetCeiling ((int(__cdecl*)(FLOOR_INFO*, int, int, int)) 0x00415900)
// 0x00415B60: GetDoor
#define LOS ((int(__cdecl*)(GAME_VECTOR*, GAME_VECTOR*)) 0x00415BB0)
// 0x00415C50: zLOS
// 0x00415F40: xLOS
// 0x00416230: ClipTarget
// 0x00416310: ObjectOnLOS
#define FlipMap ((void(__cdecl*)(void)) 0x00416610)
// 0x004166D0: RemoveRoomFlipItems
// 0x00416770: AddRoomFlipItems
void __cdecl TriggerCDTrack(__int16 value, UINT16 flags, __int16 type); // 0x004167D0
void __cdecl TriggerNormalCDTrack(__int16 value, UINT16 flags, __int16 type); // 0x00416800;
#endif // CONTROL_H_INCLUDED
================================================
FILE: game/demo.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/demo.h"
#include "game/laramisc.h"
#include "game/setup.h"
#include "game/text.h"
#include "specific/frontend.h"
#include "specific/game.h"
#include "specific/winmain.h"
#include "global/vars.h"
#ifdef FEATURE_HUD_IMPROVED
extern bool PsxBarPosEnabled;
DWORD DemoTextMode = 0;
#endif // FEATURE_HUD_IMPROVED
int __cdecl StartDemo(int levelID) {
static int DemoLevelID = 0;
if( levelID < 0 && !GF_GameFlow.num_Demos ) {
return GF_EXIT_TO_TITLE;
}
if( levelID < 0 ) {
if( DemoLevelID >= GF_GameFlow.num_Demos ) {
DemoLevelID = 0;
}
levelID = GF_DemoLevels[DemoLevelID++];
} else {
DemoLevelID = levelID;
}
START_INFO *start = &SaveGame.start[levelID];
START_INFO startBackup = *start;
start->available = 1;
start->pistolAmmo = 1000;
start->gunStatus = LGS_Armless;
start->gunType = LGT_Pistols;
SeedRandomDraw(RANDOM_SEED);
SeedRandomControl(RANDOM_SEED);
IsTitleLoaded = FALSE;
if( !InitialiseLevel(levelID, GFL_DEMO) ) {
return GF_EXIT_GAME;
}
IsLevelComplete = FALSE;
if( !IsDemoLoaded ) {
char str[64];
sprintf(str, "Level '%s' has no demo data!", GF_LevelFilesStringTable[levelID]);
S_ExitSystem(str);
}
LoadLaraDemoPos();
LaraCheatGetStuff();
SeedRandomDraw(RANDOM_SEED);
SeedRandomControl(RANDOM_SEED);
#ifdef FEATURE_HUD_IMPROVED
TEXT_STR_INFO *bottomText = NULL;
TEXT_STR_INFO *topText = NULL;
if( DemoTextMode == 1 ) {
bottomText = T_Print(0, -16, 0, GF_SpecificStringTable[SSI_DemoMode]);
} else if( DemoTextMode == 2 ) {
bottomText = T_Print(0, -16, 0, "Press any button to quit");
topText = T_Print(16, PsxBarPosEnabled ? 26 : 32, 0, "DEMO MODE");
}
#else // FEATURE_HUD_IMPROVED
// NOTE: here was the bug in the original game, wrong y coordinate and wrong align
TEXT_STR_INFO *bottomText = T_Print(0, -16, 0, GF_SpecificStringTable[SSI_DemoMode]);
#endif // FEATURE_HUD_IMPROVED
T_FlashText(bottomText, 1, 20);
T_BottomAlign(bottomText, 1);
T_CentreH(bottomText, 1);
InvDemoMode = TRUE;
int result = GameLoop(1);
InvDemoMode = FALSE;
#ifdef FEATURE_HUD_IMPROVED
T_RemovePrint(topText);
#endif // FEATURE_HUD_IMPROVED
T_RemovePrint(bottomText);
S_FadeToBlack();
*start = startBackup;
return result;
}
/*
* Inject function
*/
void Inject_Demo() {
// INJECT(0x004168E0, DoDemoSequence);
INJECT(0x00416940, StartDemo);
// INJECT(0x00416AF0, LoadLaraDemoPos);
// INJECT(0x00416BC0, GetDemoInput);
}
================================================
FILE: game/demo.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef DEMO_H_INCLUDED
#define DEMO_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define DoDemoSequence ((int(__cdecl*)(int)) 0x004168E0)
int __cdecl StartDemo(int levelID); // 0x00416940
#define LoadLaraDemoPos ((void(__cdecl*)(void)) 0x00416AF0)
#define GetDemoInput ((void(__cdecl*)(void)) 0x00416BC0)
#endif // DEMO_H_INCLUDED
================================================
FILE: game/diver.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/diver.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Diver() {
// INJECT(0x00416BF0, Harpoon);
// INJECT(0x00416C70, GetWaterSurface);
// INJECT(0x00416D80, DiverControl);
}
================================================
FILE: game/diver.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef DIVER_H_INCLUDED
#define DIVER_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x00416BF0: Harpoon
// 0x00416C70: GetWaterSurface
#define DiverControl ((void(__cdecl*)(__int16)) 0x00416D80)
#endif // DIVER_H_INCLUDED
================================================
FILE: game/dog.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/dog.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Dog() {
// INJECT(0x00417130, DogControl);
// INJECT(0x004174E0, TigerControl);
}
================================================
FILE: game/dog.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef DOG_H_INCLUDED
#define DOG_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define DogControl ((void(__cdecl*)(__int16)) 0x00417130)
#define TigerControl ((void(__cdecl*)(__int16)) 0x004174E0)
#endif // DOG_H_INCLUDED
================================================
FILE: game/dragon.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/dragon.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Dragon() {
// INJECT(0x00417780, ControlTwinkle);
// INJECT(0x00417900, CreateBartoliLight);
// INJECT(0x004179E0, DragonFire);
// INJECT(0x00417A90, DragonCollision);
// INJECT(0x00417D80, DragonBones);
// INJECT(0x00417E60, DragonControl);
// INJECT(0x004183B0, InitialiseBartoli);
// INJECT(0x004184D0, BartoliControl);
// INJECT(0x00418670, DinoControl);
}
================================================
FILE: game/dragon.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef DRAGON_H_INCLUDED
#define DRAGON_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x00417780: ControlTwinkle
// 0x00417900: CreateBartoliLight
// 0x004179E0: DragonFire
#define DragonCollision ((void(__cdecl*)(__int16, ITEM_INFO *, COLL_INFO *)) 0x00417A90)
#define DragonBones ((void(__cdecl*)(__int16)) 0x00417D80)
#define DragonControl ((void(__cdecl*)(__int16)) 0x00417E60)
#define InitialiseBartoli ((void(__cdecl*)(__int16)) 0x004183B0)
#define BartoliControl ((void(__cdecl*)(__int16)) 0x004184D0)
#define DinoControl ((void(__cdecl*)(__int16)) 0x00418670)
#endif // DRAGON_H_INCLUDED
================================================
FILE: game/draw.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/draw.h"
#include "3dsystem/3d_gen.h"
#include "3dsystem/scalespr.h"
#include "game/hair.h"
#include "specific/game.h"
#include "specific/output.h"
#include "global/vars.h"
#ifdef FEATURE_EXTENDED_LIMITS
LIGHT_INFO DynamicLights[64];
int BoundRooms[1024];
__int16 DrawRoomsArray[1024];
STATIC_INFO StaticObjects[256];
#endif // FEATURE_EXTENDED_LIMITS
#ifdef FEATURE_VIDEOFX_IMPROVED
extern DWORD AlphaBlendMode;
static int GoldenLaraAlpha = 0;
void ResetGoldenLaraAlpha() {
GoldenLaraAlpha = ( Lara.water_status == LWS_Cheat ) ? 0xFF : 0;
}
#endif // FEATURE_VIDEOFX_IMPROVED
void __cdecl DrawRooms(__int16 currentRoom) {
ROOM_INFO *room = &RoomInfo[currentRoom];
PhdWinLeft = room->left = 0;
PhdWinTop = room->top = 0;
PhdWinRight = room->right = PhdWinMaxX;
PhdWinBottom = room->bottom = PhdWinMaxY;
room->boundActive = 2;
BoundRooms[0] = currentRoom;
BoundStart = 0;
BoundEnd = 1;
DrawRoomsCount = 0;
OutsideCamera = room->flags & ROOM_OUTSIDE;
if( OutsideCamera ) {
OutsideLeft = 0;
OutsideTop = 0;
OutsideRight = PhdWinMaxX;
OutsideBottom = PhdWinMaxY;
} else {
OutsideLeft = PhdWinMaxX;
OutsideTop = PhdWinMaxY;
OutsideRight = 0;
OutsideBottom = 0;
}
UnderwaterCamera = room->flags & ROOM_UNDERWATER;
GetRoomBounds();
MidSort = 0;
if( OutsideCamera ) {
PhdWinLeft = OutsideLeft;
PhdWinRight = OutsideRight;
PhdWinBottom = OutsideBottom;
PhdWinTop = OutsideTop;
if( Objects[ID_SKYBOX].loaded ) {
// Draw skybox background
S_SetupAboveWater(UnderwaterCamera);
phd_PushMatrix();
PhdMatrixPtr->_03 = PhdMatrixPtr->_13 = PhdMatrixPtr->_23 = 0;
UINT16 *ptr = (UINT16 *)&Anims[Objects[ID_SKYBOX].animIndex].framePtr[9];
phd_RotYXZsuperpack(&ptr, 0);
#ifdef FEATURE_VIEW_IMPROVED
S_InitialisePolyList(1); // Fill backbuffer with black
#else // !FEATURE_VIEW_IMPROVED
S_InitialisePolyList(0); // Leave backbuffer uncleaned
#endif // FEATURE_VIEW_IMPROVED
S_InsertBackground(MeshPtr[Objects[ID_SKYBOX].meshIndex]);
phd_PopMatrix();
} else {
S_InitialisePolyList(1); // Fill backbuffer with black
OutsideCamera = -1;
}
} else {
#ifdef FEATURE_VIEW_IMPROVED
S_InitialisePolyList(1); // Fill backbuffer with black
#else // !FEATURE_VIEW_IMPROVED
S_InitialisePolyList(0); // Leave backbuffer uncleaned
#endif // FEATURE_VIEW_IMPROVED
}
// Draw Lara
if( Objects[ID_LARA].loaded && !(LaraItem->flags & IFL_INVISIBLE) ) {
if( RoomInfo[LaraItem->roomNumber].flags & ROOM_UNDERWATER ) {
S_SetupBelowWater(UnderwaterCamera);
} else {
S_SetupAboveWater(UnderwaterCamera);
}
MidSort = RoomInfo[LaraItem->roomNumber].boundActive >> 8;
if( MidSort ) --MidSort;
#if defined(FEATURE_VIDEOFX_IMPROVED)
if( Lara.mesh_effects ) {
if( GoldenLaraAlpha < 0xFF ) GoldenLaraAlpha += 8;
CLAMPG(GoldenLaraAlpha, 0xFF);
} else {
if( GoldenLaraAlpha > 0 ) GoldenLaraAlpha -= 8;
CLAMPL(GoldenLaraAlpha, 0);
}
if( GoldenLaraAlpha ) {
// NOTE: this is dirty trick for Golden Lara while Dozy cheat mode.
// In TR1 there is bit mask in mesh_effects for distinct meshes,
// but whole Lara must be golden here, including braid and weapons.
// The original TR1 gold color is #A09860, but it Midas room walls
// were orange tinted and bright, so to compensate all of this
// and to make gold more sweet here used different tint color.
SetMeshReflectState(ID_NONE, RGBA_MAKE(0xFF,0xC0,0x40,GoldenLaraAlpha));
}
#endif // FEATURE_VIDEOFX_IMPROVED
DrawLara(LaraItem);
#if defined(FEATURE_VIDEOFX_IMPROVED)
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
}
// Draw rooms
for( int i = 0; i < DrawRoomsCount; ++i ) {
PrintRooms(DrawRoomsArray[i]);
}
// Draw movable and static objects
for( int i = 0; i < DrawRoomsCount; ++i ) {
PrintObjects(DrawRoomsArray[i]);
}
#ifdef FEATURE_VIEW_IMPROVED
for( int i = 0; i < DrawRoomsCount; ++i ) {
RoomInfo[DrawRoomsArray[i]].boundActive = 0;
}
MidSort = 0;
#endif // FEATURE_VIEW_IMPROVED
}
void __cdecl GetRoomBounds() {
while( BoundStart != BoundEnd ) {
int roomNumber = BoundRooms[BoundStart++ % ARRAY_SIZE(BoundRooms)];
ROOM_INFO *room = &RoomInfo[roomNumber];
room->boundActive &= ~2;
MidSort = (room->boundActive >> 8) + 1;
CLAMPG(room->boundLeft, room->left)
CLAMPG(room->boundTop, room->top)
CLAMPL(room->boundRight, room->right)
CLAMPL(room->boundBottom, room->bottom)
if( !CHK_ANY(room->boundActive, 1) ) {
DrawRoomsArray[DrawRoomsCount++] = roomNumber;
room->boundActive |= 1;
if( CHK_ANY(room->flags, ROOM_OUTSIDE) ) {
OutsideCamera = ROOM_OUTSIDE;
}
}
// NOTE: The original game checks just ROOM_INSIDE flag here
if( CHK_ANY(room->flags, ROOM_OUTSIDE) || !CHK_ANY(room->flags, ROOM_INSIDE) ) {
CLAMPG(OutsideLeft, room->boundLeft)
CLAMPG(OutsideTop, room->boundTop)
CLAMPL(OutsideRight, room->boundRight)
CLAMPL(OutsideBottom, room->boundBottom)
}
if( !room->doors ) continue;
phd_PushMatrix();
phd_TranslateAbs(room->x, room->y, room->z);
for( int i = 0; i < room->doors->wCount; ++i ) {
DOOR_INFO *door = &room->doors->door[i];
if( door->x * (room->x + door->vertex[0].x - MatrixW2V._03)
+ door->y * (room->y + door->vertex[0].y - MatrixW2V._13)
+ door->z * (room->z + door->vertex[0].z - MatrixW2V._23) < 0 )
{
SetRoomBounds((__int16 *)&door->x, door->room, room);
}
}
phd_PopMatrix();
}
}
void __cdecl SetRoomBounds(__int16 *ptrObj, int roomNumber, ROOM_INFO *parent) {
ROOM_INFO *room = &RoomInfo[roomNumber];
if( room->boundLeft <= parent->left
&& room->boundRight >= parent->right
&& room->boundTop <= parent->top
&& room->boundBottom >= parent->bottom )
{
return;
}
PHD_VECTOR view[4];
int left = parent->right;
int right = parent->left;
int top = parent->bottom;
int bottom = parent->top;
int tooFar = 0;
int tooNear = 0;
for( int i=0; i<4; ++i ) {
ptrObj += 3;
PHD_MATRIX *m = PhdMatrixPtr;
int x = view[i].x = ptrObj[0] * m->_00 + ptrObj[1] * m->_01 + ptrObj[2] * m->_02 + m->_03;
int y = view[i].y = ptrObj[0] * m->_10 + ptrObj[1] * m->_11 + ptrObj[2] * m->_12 + m->_13;
int z = view[i].z = ptrObj[0] * m->_20 + ptrObj[1] * m->_21 + ptrObj[2] * m->_22 + m->_23;
if( z <= 0 ) {
++tooNear;
continue;
}
if( z > PhdFarZ ) {
++tooFar;
}
z /= PhdPersp;
if( z ) {
x = PhdWinCenterX + x / z;
y = PhdWinCenterY + y / z;
} else {
x = ( x < 0 ) ? PhdWinLeft : PhdWinRight;
y = ( y < 0 ) ? PhdWinTop : PhdWinBottom;
}
CLAMPG(left, x - 1);
CLAMPG(top, y - 1);
CLAMPL(right, x + 1);
CLAMPL(bottom, y + 1);
}
if( tooNear == 4 || tooFar == 4 ) {
return;
}
if( tooNear > 0 ) {
for( int i=0; i<4; ++i ) {
int j=(i+3)%4;
if( (view[i].z < 0) == (view[j].z < 0) ) {
continue;
}
if( view[i].x <= 0 || view[j].x <= 0 ) {
left = 0;
}
if( view[i].x >= 0 || view[j].x >= 0 ) {
right = PhdWinMaxX;
}
if( view[i].y <= 0 || view[j].y <= 0 ) {
top = 0;
}
if( view[i].y >= 0 || view[j].y >= 0 ) {
bottom = PhdWinMaxY;
}
}
}
CLAMPL(left, parent->left);
CLAMPL(top, parent->top);
CLAMPG(right, parent->right);
CLAMPG(bottom, parent->bottom);
if( left >= right || top >= bottom ) {
return;
}
if( CHK_ANY(room->boundActive, 2) ) {
CLAMPG(room->left, left);
CLAMPG(room->top, top);
CLAMPL(room->right, right);
CLAMPL(room->bottom, bottom);
} else {
BoundRooms[BoundEnd++ % ARRAY_SIZE(BoundRooms)] = roomNumber;
room->boundActive |= 2;
#ifdef FEATURE_VIEW_IMPROVED
if( !CHK_ANY(room->boundActive, 1) ) {
room->boundActive += MidSort << 8;
}
#else // FEATURE_VIEW_IMPROVED
room->boundActive += MidSort << 8;
#endif // FEATURE_VIEW_IMPROVED
room->left = left;
room->right = right;
room->top = top;
room->bottom = bottom;
}
}
void __cdecl ClipRoom(ROOM_INFO *room) {
static const int p[12][2] = {
{0, 1}, {1, 2}, {2, 3}, {3, 0},
{4, 5}, {5, 6}, {6, 7}, {7, 4},
{0, 4}, {1, 5}, {2, 6}, {3, 7},
};
int xv[8], yv[8], zv[8], clip[8];
int xMin, yMin, xMax, yMax;
int clipRoom = 0;
xv[0] = xv[3] = xv[4] = xv[7] = 0x400;
yv[0] = yv[1] = yv[2] = yv[3] = room->maxCeiling - room->y;
zv[0] = zv[1] = zv[4] = zv[5] = 0x400;
xv[1] = xv[2] = xv[5] = xv[6] = (room->ySize - 1) * 0x400;
yv[4] = yv[5] = yv[6] = yv[7] = room->minFloor - room->y;
zv[2] = zv[3] = zv[6] = zv[7] = (room->xSize - 1) * 0x400;
for( int i=0; i<8; ++i ) {
PHD_MATRIX *m = PhdMatrixPtr;
int x = xv[i];
int y = yv[i];
int z = zv[i];
xv[i] = (x * m->_00) + (y * m->_01) + (z * m->_02) + m->_03;
yv[i] = (x * m->_10) + (y * m->_11) + (z * m->_12) + m->_13;
zv[i] = (x * m->_20) + (y * m->_21) + (z * m->_22) + m->_23;
if( zv[i] > PhdFarZ ) {
clip[i] = 1;
clipRoom = 1;
} else {
clip[i] = 0;
}
}
if( !clipRoom ) {
return;
}
xMin = yMin = 0x10000000;
xMax = yMax = -0x10000000;
for( int i=0; i<12; ++i ) {
int p1 = p[i][0];
int p2 = p[i][1];
if( clip[p1] ^ clip[p2] ) {
int zDiv = (zv[p2] - zv[p1]) >> W2V_SHIFT;
if( zDiv ) {
int zNom = (PhdFarZ - zv[p1]) >> W2V_SHIFT;
int x = xv[p1] + ((((xv[p2] - xv[p1]) >> W2V_SHIFT) * zNom / zDiv) << W2V_SHIFT);
int y = yv[p1] + ((((yv[p2] - yv[p1]) >> W2V_SHIFT) * zNom / zDiv) << W2V_SHIFT);
CLAMPG(xMin, x);
CLAMPG(yMin, y);
CLAMPL(xMax, x);
CLAMPL(yMax, y);
} else {
CLAMPG(xMin, xv[p1]);
CLAMPG(xMin, xv[p2]);
CLAMPG(yMin, yv[p1]);
CLAMPG(yMin, yv[p2]);
CLAMPL(xMax, xv[p1]);
CLAMPL(xMax, xv[p2]);
CLAMPL(yMax, yv[p1]);
CLAMPL(yMax, yv[p2]);
}
}
}
xMin = PhdWinCenterX + xMin / (PhdFarZ / PhdPersp);
yMin = PhdWinCenterY + yMin / (PhdFarZ / PhdPersp);
xMax = PhdWinCenterX + xMax / (PhdFarZ / PhdPersp);
yMax = PhdWinCenterY + yMax / (PhdFarZ / PhdPersp);
if( xMin <= PhdWinRight && yMin <= PhdWinBottom && xMax >= PhdWinLeft && yMax >= PhdWinTop ) {
CLAMPL(xMin, PhdWinLeft);
CLAMPL(yMin, PhdWinTop);
CLAMPG(xMax, PhdWinRight);
CLAMPG(yMax, PhdWinBottom);
S_InsertBackPolygon(xMin, yMin, xMax, yMax);
}
}
void __cdecl PrintRooms(__int16 roomNumber) {
ROOM_INFO *room = &RoomInfo[roomNumber];
#ifdef FEATURE_VIEW_IMPROVED
if( CHK_ANY(room->boundActive, 4) ) {
return;
}
#endif // FEATURE_VIEW_IMPROVED
if( CHK_ANY(room->flags, ROOM_UNDERWATER) ) {
S_SetupBelowWater(UnderwaterCamera);
} else {
S_SetupAboveWater(UnderwaterCamera);
}
MidSort = room->boundActive >> 8;
phd_TranslateAbs(room->x, room->y, room->z);
PhdWinLeft = room->boundLeft;
PhdWinRight = room->boundRight;
PhdWinTop = room->boundTop;
PhdWinBottom = room->boundBottom;
S_LightRoom(room);
if( OutsideCamera > 0 && !CHK_ANY(room->flags, ROOM_INSIDE) ) {
S_InsertRoom(room->data, 1);
} else {
if( OutsideCamera >= 0 ) {
ClipRoom(room);
}
S_InsertRoom(room->data, 0);
}
#ifdef FEATURE_VIEW_IMPROVED
room->boundActive |= 4;
#endif // FEATURE_VIEW_IMPROVED
}
void __cdecl PrintObjects(__int16 roomNumber) {
ROOM_INFO *room = &RoomInfo[roomNumber];
if( CHK_ANY(room->flags, ROOM_UNDERWATER) ) {
S_SetupBelowWater(UnderwaterCamera);
} else {
S_SetupAboveWater(UnderwaterCamera);
}
MidSort = room->boundActive >> 8;
#ifndef FEATURE_VIEW_IMPROVED
room->boundActive = 0;
#endif // FEATURE_VIEW_IMPROVED
phd_PushMatrix();
phd_TranslateAbs(room->x, room->y, room->z);
PhdWinLeft = room->boundLeft;
PhdWinTop = room->boundTop;
PhdWinRight = room->boundRight;
PhdWinBottom = room->boundBottom;
MESH_INFO *mesh = room->mesh;
for( int i = 0; i < room->numMeshes; ++i ) {
if( !CHK_ANY(StaticObjects[mesh[i].staticNumber].flags, 2) ) {
continue;
}
phd_PushMatrix();
phd_TranslateAbs(mesh[i].x, mesh[i].y, mesh[i].z);
phd_RotY(mesh[i].yRot);
__int16 clip = S_GetObjectBounds((__int16 *)&StaticObjects[mesh[i].staticNumber].drawBounds);
if( clip ) {
S_CalculateStaticMeshLight(mesh[i].x, mesh[i].y, mesh[i].z, mesh[i].shade1, mesh[i].shade2, room);
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(mesh[i].staticNumber, -1);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons(MeshPtr[StaticObjects[mesh[i].staticNumber].meshIndex], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
}
phd_PopMatrix();
}
PhdWinLeft = 0;
PhdWinTop = 0;
PhdWinRight = PhdWinMaxX + 1;
PhdWinBottom = PhdWinMaxY + 1;
for( __int16 id = room->itemNumber; id >= 0; id = Items[id].nextItem ) {
if( Items[id].status != ITEM_INVISIBLE ) {
Objects[Items[id].objectID].drawRoutine(&Items[id]);
}
}
for( __int16 id = room->fxNumber; id >= 0; id = Effects[id].next_fx ) {
DrawEffect(id);
}
phd_PopMatrix();
room->boundLeft = PhdWinMaxX;
room->boundTop = PhdWinMaxY;
room->boundRight = 0;
room->boundBottom = 0;
}
void __cdecl DrawEffect(__int16 fx_id) {
FX_INFO *fx = &Effects[fx_id];
OBJECT_INFO *obj = &Objects[fx->object_number];
if( !obj->loaded ) return;
if( fx->object_number == ID_GLOW ) {
// NOTE: Core's hacky way to store the sprite flags in the rotation fields
S_DrawSprite((fx->pos.rotY << 16)|(fx->pos.rotX), // flags
fx->pos.x, fx->pos.y, fx->pos.z, // coordinates
Objects[ID_GLOW].meshIndex, // sprite id
fx->shade, fx->frame_number); // shade, scale
} else if( obj->nMeshes < 0 ) {
S_DrawSprite(SPR_ABS | SPR_SHADE | (obj->semi_transparent ? SPR_SEMITRANS : 0), // flags
fx->pos.x, fx->pos.y, fx->pos.z, // coordinates
obj->meshIndex - fx->frame_number, // sprite id
fx->shade, 0); // shade, scale
} else {
phd_PushMatrix();
phd_TranslateAbs(fx->pos.x, fx->pos.y, fx->pos.z);
if( PhdMatrixPtr->_23 > PhdNearZ && PhdMatrixPtr->_23 < PhdFarZ ) {
__int16 *meshPtr = NULL;
phd_RotYXZ(fx->pos.rotY, fx->pos.rotX, fx->pos.rotZ);
S_CalculateStaticLight(fx->shade);
if( obj->nMeshes ) {
meshPtr = MeshPtr[obj->meshIndex];
} else {
meshPtr = MeshPtr[fx->frame_number];
}
phd_PutPolygons(meshPtr, -1);
}
phd_PopMatrix();
}
}
void __cdecl DrawSpriteItem(ITEM_INFO *item) {
OBJECT_INFO *obj;
phd_PushUnitMatrix(); // NOTE: this push is workaround for sprites with no matrix
S_CalculateStaticMeshLight(item->pos.x, item->pos.y, item->pos.z,
item->shade1, item->shade2, &RoomInfo[item->roomNumber]);
phd_PopMatrix(); // NOTE: this pop is workaround for sprites with no matrix
obj = &Objects[item->objectID];
// NOTE: SPR_ITEM is not presented in the original game
S_DrawSprite(SPR_ITEM | SPR_ABS | SPR_SHADE | (obj->semi_transparent ? SPR_SEMITRANS : 0),
item->pos.x, item->pos.y, item->pos.z,
obj->meshIndex - item->frameNumber,
LsAdder + 0x1000, 0);
}
void __cdecl DrawDummyItem(ITEM_INFO *item) {
}
void __cdecl DrawAnimatingItem(ITEM_INFO *item) {
static __int16 no_rotation[12] = {0};
__int16 *frames[2] = {0};
int rate = 0;
DWORD bit = 1;
int frac = GetFrames(item, frames, &rate);
OBJECT_INFO *obj = &Objects[item->objectID];
if( obj->shadowSize ) {
S_PrintShadow(obj->shadowSize, frames[0], item);
}
phd_PushMatrix();
phd_TranslateAbs(item->pos.x, item->pos.y, item->pos.z);
phd_RotYXZ(item->pos.rotY, item->pos.rotX, item->pos.rotZ);
int clip = S_GetObjectBounds(frames[0]);
if( clip ) {
CalculateObjectLighting(item, frames[0]);
__int16 *rots = item->data ? (__int16 *)item->data : no_rotation;
__int16 **meshPtr = &MeshPtr[obj->meshIndex];
int *bonePtr = &AnimBones[obj->boneIndex];
if( frac ) {
InitInterpolate(frac, rate);
phd_TranslateRel_ID(frames[0][6], frames[0][7], frames[0][8], frames[1][6], frames[1][7], frames[1][8]);
UINT16 *rot1 = (UINT16 *)&frames[0][9];
UINT16 *rot2 = (UINT16 *)&frames[1][9];
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
if( CHK_ANY(item->meshBits, 1) ) {
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(item->objectID, 0);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons_I(meshPtr[0], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
}
for( int i = 1; i < obj->nMeshes; ++i ) {
DWORD state = *bonePtr;
if( CHK_ANY(state, 1) ) {
phd_PopMatrix_I();
}
if( CHK_ANY(state, 2) ) {
phd_PushMatrix_I();
}
phd_TranslateRel_I(bonePtr[1], bonePtr[2], bonePtr[3]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
if( CHK_ANY(state, 0x1C) ) {
if( CHK_ANY(state, 0x08) ) {
phd_RotY_I(*(rots++));
}
if( CHK_ANY(state, 0x04) ) {
phd_RotX_I(*(rots++));
}
if( CHK_ANY(state, 0x10) ) {
phd_RotZ_I(*(rots++));
}
}
bonePtr+=4;
bit <<= 1;
if( CHK_ANY(item->meshBits, bit) ) {
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(item->objectID, i);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons_I(meshPtr[i], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
}
}
} else {
phd_TranslateRel(frames[0][6], frames[0][7], frames[0][8]);
UINT16 *rot = (UINT16 *)&frames[0][9];
phd_RotYXZsuperpack(&rot, 0);
if( CHK_ANY(item->meshBits, 1) ) {
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(item->objectID, 0);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons(meshPtr[0], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
}
for( int i = 1; i < obj->nMeshes; ++i) {
DWORD state = *bonePtr;
if( CHK_ANY(state, 1) ) {
phd_PopMatrix();
}
if( CHK_ANY(state, 2) ) {
phd_PushMatrix();
}
phd_TranslateRel(bonePtr[1], bonePtr[2], bonePtr[3]);
phd_RotYXZsuperpack(&rot, 0);
if( CHK_ANY(state, 0x1C) ) {
if( CHK_ANY(state, 0x08) ) {
phd_RotY(*(rots++));
}
if( CHK_ANY(state, 0x04) ) {
phd_RotX(*(rots++));
}
if( CHK_ANY(state, 0x10) ) {
phd_RotZ(*(rots++));
}
}
bonePtr += 4;
bit <<= 1;
if( CHK_ANY(item->meshBits, bit) ) {
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(item->objectID, i);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons(meshPtr[i], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
}
}
}
}
phd_PopMatrix();
}
void __cdecl DrawLaraInt(ITEM_INFO *item, __int16 *frame1, __int16 *frame2, int frac, int rate) {
PHD_MATRIX matrix;
UINT16 *rot1, *rot2, *rot1copy, *rot2copy;
int frame, *bones;
OBJECT_INFO *obj = &Objects[item->objectID];
__int16 *bounds = GetBoundsAccurate(item);
if( Lara.skidoo == -1 ) {
S_PrintShadow(obj->shadowSize, bounds, item);
}
matrix = *PhdMatrixPtr;
phd_PushMatrix();
phd_TranslateAbs(item->pos.x, item->pos.y, item->pos.z);
phd_RotYXZ(item->pos.rotY, item->pos.rotX, item->pos.rotZ);
int clip = S_GetObjectBounds(frame1);
if( !clip ) {
phd_PopMatrix();
return;
}
phd_PushMatrix();
CalculateObjectLighting(item, frame1);
bones = &AnimBones[obj->boneIndex];
rot1 = (UINT16 *)frame1 + 9;
rot2 = (UINT16 *)frame2 + 9;
InitInterpolate(frac, rate);
phd_TranslateRel_ID(frame1[6], frame1[7], frame1[8], frame2[6], frame2[7], frame2[8]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[0], clip);
phd_PushMatrix_I();
phd_TranslateRel_I(bones[1], bones[2], bones[3]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[1], clip);
phd_TranslateRel_I(bones[5], bones[6], bones[7]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[2], clip);
phd_TranslateRel_I(bones[9], bones[10], bones[11]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[3], clip);
phd_PopMatrix_I();
phd_PushMatrix_I();
phd_TranslateRel_I(bones[13], bones[14], bones[15]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[4], clip);
phd_TranslateRel_I(bones[17], bones[18], bones[19]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[5], clip);
phd_TranslateRel_I(bones[21], bones[22], bones[23]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[6], clip);
phd_PopMatrix_I();
phd_TranslateRel_I(bones[25], bones[26], bones[27]);
if ( Lara.weapon_item != -1 && Lara.gun_type == LGT_M16
&& (Items[Lara.weapon_item].currentAnimState == 0
|| Items[Lara.weapon_item].currentAnimState == 2
|| Items[Lara.weapon_item].currentAnimState == 4) )
{
frame = Lara.right_arm.frame_number * (Anims[Lara.right_arm.anim_number].interpolation >> 8) + 9;
rot1 = rot2 = (UINT16 *)&Lara.right_arm.frame_base[frame];
phd_RotYXZsuperpack_I(&rot1, &rot2, 7);
} else {
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
}
phd_RotYXZ_I(Lara.torso_y_rot, Lara.torso_x_rot, Lara.torso_z_rot);
phd_PutPolygons_I(Lara.mesh_ptrs[7], clip);
phd_PushMatrix_I();
phd_TranslateRel_I(bones[53], bones[54], bones[55]);
rot1copy = rot1;
rot2copy = rot2;
phd_RotYXZsuperpack_I(&rot1, &rot2, 6);
rot1 = rot1copy;
rot2 = rot2copy;
phd_RotYXZ_I(Lara.head_y_rot, Lara.head_x_rot, Lara.head_z_rot);
phd_PutPolygons_I(Lara.mesh_ptrs[14], clip);
*PhdMatrixPtr = matrix;
DrawHair();
phd_PopMatrix_I();
if( Lara.back_gun ) {
phd_PushMatrix_I();
int *bone = &AnimBones[Objects[Lara.back_gun].boneIndex];
phd_TranslateRel_I(bone[53], bone[54], bone[55]);
rot2copy = (UINT16 *)Objects[Lara.back_gun].frameBase + 9;
rot1copy = (UINT16 *)Objects[Lara.back_gun].frameBase + 9;
phd_RotYXZsuperpack_I(&rot1copy, &rot2copy, 14);
phd_PutPolygons_I(MeshPtr[Objects[Lara.back_gun].meshIndex + 14], clip);
phd_PopMatrix_I();
}
int gunType = LGT_Unarmed;
if( Lara.gun_status == LGS_Ready
|| Lara.gun_status == LGS_Special
|| Lara.gun_status == LGS_Draw
|| Lara.gun_status == LGS_Undraw )
{
gunType = Lara.gun_type;
}
switch( gunType ) {
case LGT_Unarmed:
case LGT_Flare:
phd_PushMatrix_I();
phd_TranslateRel_I(bones[29], bones[30], bones[31]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[8], clip);
phd_TranslateRel_I(bones[33], bones[34], bones[35]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[9], clip);
phd_TranslateRel_I(bones[37], bones[38], bones[39]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[10], clip);
phd_PopMatrix_I();
phd_PushMatrix_I();
phd_TranslateRel_I(bones[41], bones[42], bones[43]);
if( Lara.flare_control_left ) {
frame = (Anims[Lara.left_arm.anim_number].interpolation >> 8) * (Lara.left_arm.frame_number - Anims[Lara.left_arm.anim_number].frameBase) + 9;
rot1 = rot2 = (UINT16 *)&Lara.left_arm.frame_base[frame];
phd_RotYXZsuperpack_I(&rot1, &rot2, 11);
} else {
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
}
phd_PutPolygons_I(Lara.mesh_ptrs[11], clip);
phd_TranslateRel_I(bones[45], bones[46], bones[47]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[12], clip);
phd_TranslateRel_I(bones[49], bones[50], bones[51]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[13], clip);
if( Lara.gun_type == LGT_Flare ) {
if( Lara.left_arm.flash_gun ) {
phd_TranslateRel_I(11, 32, 80);
phd_RotX_I(-90 * PHD_DEGREE);
phd_RotY_I(2 * GetRandomDraw());
S_CalculateStaticLight(0x800);
phd_PutPolygons_I(MeshPtr[Objects[ID_FLARE_FIRE].meshIndex], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
if( AlphaBlendMode ) {
int shade = (GetRandomDraw() & 0xFFF) + 0x1000;
DWORD flags = GLOW_FLARE_COLOR;
flags |= SPR_BLEND_ADD|SPR_TINT|SPR_SHADE|SPR_SEMITRANS;
S_DrawSprite(flags, 0, 0, 0, Objects[ID_GLOW].meshIndex, shade, 0);
}
#endif // FEATURE_VIDEOFX_IMPROVED
}
}
break;
case LGT_Pistols:
case LGT_Magnums:
case LGT_Uzis:
phd_PushMatrix_I();
phd_TranslateRel_I(bones[29], bones[30], bones[31]);
InterpolateArmMatrix();
phd_RotYXZ(Lara.right_arm.y_rot, Lara.right_arm.x_rot, Lara.right_arm.z_rot);
frame = (Anims[Lara.right_arm.anim_number].interpolation >> 8) * (Lara.right_arm.frame_number - Anims[Lara.right_arm.anim_number].frameBase) + 9;
rot1 = (UINT16 *)&Lara.right_arm.frame_base[frame];
phd_RotYXZsuperpack(&rot1, 8);
phd_PutPolygons(Lara.mesh_ptrs[8], clip);
phd_TranslateRel(bones[33], bones[34], bones[35]);
phd_RotYXZsuperpack(&rot1, 0);
phd_PutPolygons(Lara.mesh_ptrs[9], clip);
phd_TranslateRel(bones[37], bones[38], bones[39]);
phd_RotYXZsuperpack(&rot1, 0);
phd_PutPolygons(Lara.mesh_ptrs[10], clip);
if( Lara.right_arm.flash_gun ) {
matrix = *PhdMatrixPtr;
}
phd_PopMatrix_I();
phd_PushMatrix_I();
phd_TranslateRel_I(bones[41], bones[42], bones[43]);
InterpolateArmMatrix();
phd_RotYXZ(Lara.left_arm.y_rot, Lara.left_arm.x_rot, Lara.left_arm.z_rot);
frame = (Anims[Lara.left_arm.anim_number].interpolation >> 8) * (Lara.left_arm.frame_number - Anims[Lara.left_arm.anim_number].frameBase) + 9;
rot1 = (UINT16 *)&Lara.left_arm.frame_base[frame];
phd_RotYXZsuperpack(&rot1, 11);
phd_PutPolygons(Lara.mesh_ptrs[11], clip);
phd_TranslateRel(bones[45], bones[46], bones[47]);
phd_RotYXZsuperpack(&rot1, 0);
phd_PutPolygons(Lara.mesh_ptrs[12], clip);
phd_TranslateRel(bones[49], bones[50], bones[51]);
phd_RotYXZsuperpack(&rot1, 0);
phd_PutPolygons(Lara.mesh_ptrs[13], clip);
if( Lara.left_arm.flash_gun ) {
DrawGunFlash(gunType, clip);
}
if( Lara.right_arm.flash_gun ) {
*PhdMatrixPtr = matrix;
DrawGunFlash(gunType, clip);
}
break;
case LGT_Shotgun:
case LGT_M16:
case LGT_Grenade:
case LGT_Harpoon:
phd_PushMatrix_I();
phd_TranslateRel_I(bones[29], bones[30], bones[31]);
frame = Lara.right_arm.frame_number * (Anims[Lara.right_arm.anim_number].interpolation >> 8) + 9;
rot1 = rot2 = (UINT16 *)&Lara.right_arm.frame_base[frame];
phd_RotYXZsuperpack_I(&rot1, &rot2, 8);
phd_PutPolygons_I(Lara.mesh_ptrs[8], clip);
phd_TranslateRel_I(bones[33], bones[34], bones[35]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[9], clip);
phd_TranslateRel_I(bones[37], bones[38], bones[39]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[10], clip);
if( Lara.right_arm.flash_gun ) {
matrix = *PhdMatrixPtr;
}
phd_PopMatrix_I();
phd_PushMatrix_I();
phd_TranslateRel_I(bones[41], bones[42], bones[43]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[11], clip);
phd_TranslateRel_I(bones[45], bones[46], bones[47]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[12], clip);
phd_TranslateRel_I(bones[49], bones[50], bones[51]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
phd_PutPolygons_I(Lara.mesh_ptrs[13], clip);
if( Lara.right_arm.flash_gun ) {
*PhdMatrixPtr = matrix;
DrawGunFlash(gunType, clip);
}
break;
default:
break;
}
phd_PopMatrix();
phd_PopMatrix();
phd_PopMatrix();
}
void __cdecl phd_RotYXZsuperpack(UINT16 **pptr, int index) {
for( int i = 0; i < index; ++i ) {
if( (**pptr >> 14) == 0 )
*pptr += 2;
else
++*pptr;
}
switch( **pptr >> 14 ) {
case 0 :
phd_RotYXZpack(((UINT16)(*pptr)[0] << 16) + (UINT16)(*pptr)[1]);
*pptr += 2;
break;
case 1 :
phd_RotX(**pptr << 6);
++*pptr;
break;
case 2 :
phd_RotY(**pptr << 6);
++*pptr;
break;
case 3 :
phd_RotZ(**pptr << 6);
++*pptr;
break;
}
}
void __cdecl phd_PutPolygons_I(__int16 *ptrObj, int clip) {
phd_PushMatrix();
InterpolateMatrix();
phd_PutPolygons(ptrObj, clip);
phd_PopMatrix();
}
void __cdecl DrawGunFlash(int weapon, int clip) {
__int16 light;
int len;
int off;
switch( weapon ) {
case LGT_Shotgun:
return;
case LGT_Flare:
phd_TranslateRel(11, 32, 80);
phd_RotX(-90 * PHD_DEGREE);
phd_RotY(2 * GetRandomDraw());
S_CalculateStaticLight(0x800);
phd_PutPolygons(MeshPtr[Objects[ID_FLARE_FIRE].meshIndex], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
if( AlphaBlendMode ) {
int shade = (GetRandomDraw() & 0xFFF) + 0x1000;
DWORD flags = GLOW_FLARE_COLOR;
flags |= SPR_BLEND_ADD|SPR_TINT|SPR_SHADE|SPR_SEMITRANS;
S_DrawSprite(flags, 0, 0, 0, Objects[ID_GLOW].meshIndex, shade, 0);
}
#endif // FEATURE_VIDEOFX_IMPROVED
return;
case LGT_M16:
phd_TranslateRel(0, 400, 99);
phd_RotYXZ(0, -85 * PHD_DEGREE, (2 * GetRandomDraw() & 0x4000) + 0x2000);
S_CalculateStaticLight(0xA00);
phd_PutPolygons(MeshPtr[Objects[ID_M16_FLASH].meshIndex], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
if( AlphaBlendMode ) {
DWORD flags = GLOW_M16_COLOR;
flags |= SPR_BLEND_ADD|SPR_TINT|SPR_SCALE|SPR_SEMITRANS;
S_DrawSprite(flags, 0, 0, -65, Objects[ID_GLOW].meshIndex, 0, 0x200);
}
#endif // FEATURE_VIDEOFX_IMPROVED
return;
case LGT_Magnums:
light = 0x1000;
len = 215;
off = 65;
break;
case LGT_Uzis:
light = 0xA00;
len = 200;
off = 50;
break;
default:
light = 0x1400;
len = 185;
off = 40;
break;
}
phd_TranslateRel(0, len, off);
phd_RotYXZ(0, -90 * PHD_DEGREE, 2 * GetRandomDraw());
S_CalculateStaticLight(light);
phd_PutPolygons(MeshPtr[Objects[ID_GUN_FLASH].meshIndex], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
if( AlphaBlendMode ) {
DWORD flags = GLOW_PISTOL_COLOR;
flags |= SPR_BLEND_ADD|SPR_TINT|SPR_SCALE|SPR_SEMITRANS;
S_DrawSprite(flags, 0, 0, 0, Objects[ID_GLOW].meshIndex, 0, 0x200);
}
#endif // FEATURE_VIDEOFX_IMPROVED
}
void __cdecl AddDynamicLight(int x, int y, int z, int intensity, int falloff) {
int idx = ( DynamicLightCount < ARRAY_SIZE(DynamicLights) ) ? DynamicLightCount++ : 0;
DynamicLights[idx].x = x;
DynamicLights[idx].y = y;
DynamicLights[idx].z = z;
DynamicLights[idx].intensity1 = intensity;
DynamicLights[idx].fallOff1 = falloff;
}
/*
* Inject function
*/
void Inject_Draw() {
// INJECT(0x00418920, DrawPhaseCinematic);
// INJECT(0x00418960, DrawPhaseGame);
INJECT(0x004189A0, DrawRooms);
INJECT(0x00418C50, GetRoomBounds);
INJECT(0x00418E20, SetRoomBounds);
INJECT(0x004191A0, ClipRoom);
INJECT(0x00419580, PrintRooms);
INJECT(0x00419640, PrintObjects);
INJECT(0x00419870, DrawEffect);
INJECT(0x004199C0, DrawSpriteItem);
// INJECT(----------, DrawDummyItem);
INJECT(0x00419A50, DrawAnimatingItem);
// INJECT(0x00419DD0, DrawLara);
INJECT(0x0041AB00, DrawLaraInt);
// INJECT(0x0041B6F0, InitInterpolate);
// INJECT(0x0041B730, phd_PopMatrix_I);
// INJECT(0x0041B760, phd_PushMatrix_I);
// INJECT(0x0041B790, phd_RotY_I);
// INJECT(0x0041B7D0, phd_RotX_I);
// INJECT(0x0041B810, phd_RotZ_I);
// INJECT(0x0041B850, phd_TranslateRel_I);
// INJECT(0x0041B8A0, phd_TranslateRel_ID);
// INJECT(0x0041B8F0, phd_RotYXZ_I);
// INJECT(0x0041B940, phd_RotYXZsuperpack_I);
INJECT(0x0041B980, phd_RotYXZsuperpack);
INJECT(0x0041BA30, phd_PutPolygons_I);
// INJECT(0x0041BA60, InterpolateMatrix);
// INJECT(0x0041BC10, InterpolateArmMatrix);
INJECT(0x0041BD10, DrawGunFlash);
// INJECT(0x0041BE80, CalculateObjectLighting);
// INJECT(0x0041BF70, GetFrames);
// INJECT(0x0041C010, GetBoundsAccurate);
// INJECT(0x0041C090, GetBestFrame);
INJECT(0x0041C0D0, AddDynamicLight);
}
================================================
FILE: game/draw.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef DRAW_H_INCLUDED
#define DRAW_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define DrawPhaseCinematic ((int(__cdecl*)(void)) 0x00418920)
#define DrawPhaseGame ((int(__cdecl*)(void)) 0x00418960)
void __cdecl DrawRooms(__int16 currentRoom); // 0x004189A0
void __cdecl GetRoomBounds(); // 0x00418C50
void __cdecl SetRoomBounds(__int16 *ptrObj, int roomNumber, ROOM_INFO *parent); // 0x00418E20
void __cdecl ClipRoom(ROOM_INFO *room); // 0x004191A0
void __cdecl PrintRooms(__int16 roomNumber); // 0x00419580
void __cdecl PrintObjects(__int16 roomNumber); // 0x00419640
void __cdecl DrawEffect(__int16 fx_id); // 0x00419870
void __cdecl DrawSpriteItem(ITEM_INFO *item); // 0x004199C0
void __cdecl DrawDummyItem(ITEM_INFO *item);
void __cdecl DrawAnimatingItem(ITEM_INFO *item); // 0x00419A50
#define DrawLara ((void(__cdecl*)(ITEM_INFO*)) 0x00419DD0)
void __cdecl DrawLaraInt(ITEM_INFO *item, __int16 *frame1, __int16 *frame2, int frac, int rate);
#define InitInterpolate ((void(__cdecl*)(int, int)) 0x0041B6F0)
#define phd_PopMatrix_I ((void(__cdecl*)(void)) 0x0041B730)
#define phd_PushMatrix_I ((void(__cdecl*)(void)) 0x0041B760)
#define phd_RotY_I ((void(__cdecl*)(__int16)) 0x0041B790)
#define phd_RotX_I ((void(__cdecl*)(__int16)) 0x0041B7D0)
#define phd_RotZ_I ((void(__cdecl*)(__int16)) 0x0041B810)
#define phd_TranslateRel_I ((void(__cdecl*)(int, int, int)) 0x0041B850)
#define phd_TranslateRel_ID ((void(__cdecl*)(int, int, int, int, int, int)) 0x0041B8A0)
#define phd_RotYXZ_I ((void(__cdecl*)(int, int, int)) 0x0041B8F0)
#define phd_RotYXZsuperpack_I ((void(__cdecl*)(UINT16**, UINT16**, int)) 0x0041B940)
void __cdecl phd_RotYXZsuperpack(UINT16 **pptr, int index); // 0x0041B980
void __cdecl phd_PutPolygons_I(__int16 *ptrObj, int clip); // 0x0041BA30
#define InterpolateMatrix ((void(__cdecl*)(void)) 0x0041BA60)
#define InterpolateArmMatrix ((void(__cdecl*)(void)) 0x0041BC10)
void __cdecl DrawGunFlash(int weapon, int clip);
#define CalculateObjectLighting ((void(__cdecl*)(ITEM_INFO*, __int16*)) 0x0041BE80)
#define GetFrames ((int(__cdecl*)(ITEM_INFO*, __int16**, int*)) 0x0041BF70)
#define GetBoundsAccurate ((__int16*(__cdecl*)(ITEM_INFO*)) 0x0041C010)
#define GetBestFrame ((__int16*(__cdecl*)(ITEM_INFO*)) 0x0041C090)
void __cdecl AddDynamicLight(int x, int y, int z, int intensity, int falloff); // 0x0041C0D0
#endif // DRAW_H_INCLUDED
================================================
FILE: game/eel.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/eel.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Eel() {
// INJECT(0x0041C120, BigEelControl);
// INJECT(0x0041C2C0, EelControl);
}
================================================
FILE: game/eel.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef EEL_H_INCLUDED
#define EEL_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define BigEelControl ((void(__cdecl*)(__int16)) 0x0041C120)
#define EelControl ((void(__cdecl*)(__int16)) 0x0041C2C0)
#endif // EEL_H_INCLUDED
================================================
FILE: game/effects.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Copyright (c) 2020 ChocolateFan
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/effects.h"
#include "3dsystem/3d_gen.h"
#include "3dsystem/phd_math.h"
#include "game/control.h"
#include "game/draw.h"
#include "game/hair.h"
#include "game/invtext.h"
#include "game/items.h"
#include "game/sound.h"
#include "game/sphere.h"
#include "specific/game.h"
#include "specific/output.h"
#include "specific/sndpc.h"
#include "global/vars.h"
int __cdecl ItemNearLara(PHD_3DPOS *pos, int distance) {
int dx, dy, dz;
__int16 *frame;
dx = pos->x - LaraItem->pos.x;
dy = pos->y - LaraItem->pos.y;
dz = pos->z - LaraItem->pos.z;
if (ABS(dx) <= distance && ABS(dz) <= distance && ABS(dy) <= 3072 && SQR(dx) + SQR(dz) <= SQR(distance)) {
frame = GetBoundsAccurate(LaraItem);
if (dy >= frame[2] && dy <= frame[3] + 100)
return 1;
}
return 0;
}
void __cdecl SoundEffects() {
DWORD i;
for (i = 0; i < SoundFxCount; ++i) {
if ((FlipStatus && CHK_ANY(SoundFx[i].flags, 0x40)) || (!FlipStatus && CHK_ANY(SoundFx[i].flags, 0x80)))
PlaySoundEffect(SoundFx[i].data, (PHD_3DPOS *) &SoundFx[i].x, 0);
}
if (FlipEffect != -1)
(*SfxFunctions[FlipEffect])(NULL);
SOUND_EndScene();
}
__int16 __cdecl DoBloodSplat(int x, int y, int z, __int16 speed, __int16 direction, __int16 roomID) {
__int16 fxID;
FX_INFO *fx;
fxID = CreateEffect(roomID);
if (fxID != -1) {
fx = &Effects[fxID];
fx->pos.x = x;
fx->pos.y = y;
fx->pos.z = z;
fx->pos.rotY = direction;
fx->speed = speed;
fx->frame_number = 0;
fx->object_number = ID_BLOOD;
fx->counter = 0;
}
return fxID;
}
void __cdecl DoLotsOfBlood(int x, int y, int z, __int16 speed, __int16 direction, __int16 roomID, int number) {
int i;
for (i = 0; i < number; ++i)
DoBloodSplat(x - 512 * GetRandomDraw() / 32768 + 256,
y - 512 * GetRandomDraw() / 32768 + 256,
z - 512 * GetRandomDraw() / 32768 + 256,
speed,
direction,
roomID);
}
void __cdecl ControlBlood1(__int16 fxID) {
FX_INFO *fx;
fx = &Effects[fxID];
fx->pos.x += fx->speed * phd_sin(fx->pos.rotY) >> W2V_SHIFT;
fx->pos.z += fx->speed * phd_cos(fx->pos.rotY) >> W2V_SHIFT;
++fx->counter;
if (fx->counter == 4) {
--fx->frame_number;
fx->counter = 0;
if (fx->frame_number <= Objects[fx->object_number].nMeshes)
KillEffect(fxID);
}
}
void __cdecl ControlExplosion1(__int16 fxID) {
FX_INFO *fx;
fx = &Effects[fxID];
++fx->counter;
if (fx->counter == 2) {
--fx->frame_number;
fx->counter = 0;
if (fx->frame_number <= Objects[fx->object_number].nMeshes) {
KillEffect(fxID);
} else {
AddDynamicLight(fx->pos.x, fx->pos.y, fx->pos.z, 13, 11);
}
} else {
AddDynamicLight(fx->pos.x, fx->pos.y, fx->pos.z, 12, 10);
}
}
void __cdecl Richochet(GAME_VECTOR *pos) {
__int16 fxID = CreateEffect(pos->roomNumber);
if( fxID < 0 ) {
return;
}
FX_INFO *fx = &Effects[fxID];
fx->pos.x = pos->x;
fx->pos.y = pos->y;
fx->pos.z = pos->z;
fx->counter = 4;
fx->object_number = ID_RICOCHET;
fx->frame_number = -3 * GetRandomDraw() / 0x8000;
PlaySoundEffect(10, &fx->pos, 0);
}
void __cdecl ControlRichochet1(__int16 fxID) {
FX_INFO *fx;
fx = &Effects[fxID];
--fx->counter;
if (!fx->counter)
KillEffect(fxID);
}
void __cdecl CreateBubble(PHD_3DPOS *pos, __int16 roomNumber) {
__int16 fxID = CreateEffect(roomNumber);
if( fxID < 0 ) return;
FX_INFO *fx = &Effects[fxID];
fx->pos = *pos;
fx->speed = ((GetRandomDraw() * 6) >> 15) + 10;
fx->frame_number = -((GetRandomDraw() * 3) >> 15);
fx->object_number = ID_BUBBLES;
}
void __cdecl LaraBubbles(ITEM_INFO *item) {
#ifdef FEATURE_CHEAT
if( Lara.water_status == LWS_Cheat ) {
return;
}
#endif // FEATURE_CHEAT
int counter = GetRandomDraw() * 3 / 0x8000;
if( !counter ) return;
PHD_VECTOR pos;
pos.x = 0;
pos.y = 0;
pos.z = 50;
PlaySoundEffect(37, &item->pos, SFX_UNDERWATER);
GetJointAbsPosition(item, &pos, 14);
while( counter-- > 0 ) CreateBubble((PHD_3DPOS *)&pos, item->roomNumber);
}
void __cdecl ControlBubble1(__int16 fxID) {
FX_INFO *fx;
int x, y, z, ceiling;
__int16 roomID;
FLOOR_INFO *floor;
fx = &Effects[fxID];
fx->pos.rotY += 9 * PHD_DEGREE;
fx->pos.rotX += 13 * PHD_DEGREE;
x = fx->pos.x + (11 * phd_sin(fx->pos.rotY) >> W2V_SHIFT);
y = fx->pos.y - fx->speed;
z = fx->pos.z + (8 * phd_cos(fx->pos.rotX) >> W2V_SHIFT);
roomID = fx->room_number;
floor = GetFloor(x, y, z, &roomID);
if (floor && CHK_ANY(RoomInfo[roomID].flags, ROOM_UNDERWATER)) {
ceiling = GetCeiling(floor, x, y, z);
if (ceiling != -32512 && y > ceiling) {
if (fx->room_number != roomID)
EffectNewRoom(fxID, roomID);
fx->pos.x = x;
fx->pos.y = y;
fx->pos.z = z;
return;
}
}
KillEffect(fxID);
}
void __cdecl Splash(ITEM_INFO *item) {
int y, i;
__int16 roomID, fxID;
FX_INFO *fx;
y = GetWaterHeight(item->pos.x, item->pos.y, item->pos.z, item->roomNumber);
roomID = item->roomNumber;
GetFloor(item->pos.x, item->pos.y, item->pos.z, &roomID);
for (i = 0; i < 10; ++i) {
fxID = CreateEffect(roomID);
if (fxID != -1) {
fx = &Effects[fxID];
fx->pos.x = item->pos.x;
fx->pos.y = y;
fx->pos.z = item->pos.z;
fx->pos.rotY = 2 * GetRandomDraw() - 32768;
fx->frame_number = 0;
fx->object_number = ID_SPLASH;
fx->speed = GetRandomDraw() / 256;
}
}
}
void WadeSplash(ITEM_INFO *item, int height) {
return; // NULL function
}
void __cdecl ControlSplash1(__int16 fxID) {
FX_INFO *fx;
fx = &Effects[fxID];
--fx->frame_number;
if (fx->frame_number <= Objects[fx->object_number].nMeshes) {
KillEffect(fxID);
} else {
fx->pos.z += fx->speed * phd_cos(fx->pos.rotY) >> W2V_SHIFT;
fx->pos.x += fx->speed * phd_sin(fx->pos.rotY) >> W2V_SHIFT;
}
}
void __cdecl ControlWaterSprite(__int16 fxID) {
FX_INFO *fx;
fx = &Effects[fxID];
--fx->counter;
if (!CHK_ANY(fx->counter, 3)) {
--fx->frame_number;
if (fx->frame_number <= Objects[fx->object_number].nMeshes)
fx->frame_number = 0;
}
if (fx->counter && fx->fallspeed <= 0) {
fx->pos.z += fx->speed * phd_cos(fx->pos.rotY) >> W2V_SHIFT;
fx->pos.x += fx->speed * phd_sin(fx->pos.rotY) >> W2V_SHIFT;
if (fx->fallspeed) {
fx->pos.y += fx->fallspeed;
fx->fallspeed += 6;
}
} else {
KillEffect(fxID);
}
}
void __cdecl ControlSnowSprite(__int16 fxID) {
FX_INFO *fx;
fx = &Effects[fxID];
--fx->frame_number;
if (fx->frame_number <= Objects[fx->object_number].nMeshes) {
KillEffect(fxID);
} else {
fx->pos.z += fx->speed * phd_cos(fx->pos.rotY) >> W2V_SHIFT;
fx->pos.x += fx->speed * phd_sin(fx->pos.rotY) >> W2V_SHIFT;
if (fx->fallspeed) {
fx->pos.y += fx->fallspeed;
fx->fallspeed += 6;
}
}
}
void __cdecl ControlHotLiquid(__int16 fxID) {
FX_INFO *fx;
__int16 roomID;
int height;
fx = &Effects[fxID];
--fx->frame_number;
if (fx->frame_number <= Objects[ID_HOT_LIQUID].nMeshes)
fx->frame_number = 0;
fx->pos.y += fx->fallspeed;
fx->fallspeed += 6;
roomID = fx->room_number;
height = GetHeight(GetFloor(fx->pos.x, fx->pos.y, fx->pos.z, &roomID), fx->pos.x, fx->pos.y, fx->pos.z);
if (fx->pos.y >= height) {
PlaySoundEffect(285, &fx->pos, 0);
fx->object_number = ID_SPLASH;
fx->pos.y = height;
fx->pos.rotY = 2 * GetRandomDraw();
fx->fallspeed = 0;
fx->speed = 50;
} else {
if (fx->room_number != roomID)
EffectNewRoom(fxID, roomID);
PlaySoundEffect(284, &fx->pos, 0);
}
}
void __cdecl WaterFall(__int16 itemID) {
ITEM_INFO *item;
__int16 fxID;
FX_INFO *fx;
item = &Items[itemID];
if (ABS(item->pos.x - LaraItem->pos.x) <= 10240 &&
ABS(item->pos.z - LaraItem->pos.z) <= 10240 &&
ABS(item->pos.y - LaraItem->pos.y) <= 10240)
{
S_CalculateLight(item->pos.x, item->pos.y, item->pos.z, item->roomNumber);
fxID = CreateEffect(item->roomNumber);
if (fxID != -1) {
fx = &Effects[fxID];
fx->pos.x = item->pos.x + 1024 * (GetRandomDraw() - 16384) / 32767;
fx->pos.z = item->pos.z + 1024 * (GetRandomDraw() - 16384) / 32767;
fx->pos.y = item->pos.y;
fx->speed = 0;
fx->frame_number = 0;
fx->shade = LsAdder;
fx->object_number = ID_SPLASH;
}
PlaySoundEffect(79, &item->pos, 0);
}
}
void __cdecl finish_level_effect(ITEM_INFO *item) {
IsLevelComplete = TRUE;
}
void __cdecl turn180_effect(ITEM_INFO *item) {
item->pos.rotX = -item->pos.rotX;
item->pos.rotY += PHD_180;
}
void __cdecl floor_shake_effect(ITEM_INFO *item) {
int dx, dy, dz;
dx = item->pos.x - Camera.pos.x;
dy = item->pos.y - Camera.pos.y;
dz = item->pos.z - Camera.pos.z;
if (ABS(dx) < 16384 && ABS(dy) < 16384 && ABS(dz) < 16384)
Camera.bounce = 100 * (SQR(1024) - (SQR(dx) + SQR(dy) + SQR(dz)) / 256) / SQR(1024);
}
void __cdecl lara_normal_effect(ITEM_INFO *item) {
item->currentAnimState = AS_STOP;
item->goalAnimState = AS_STOP;
item->animNumber = 11;
item->frameNumber = Anims[item->animNumber].frameBase;
Camera.type = CAM_Chase;
AlterFOV(80 * PHD_DEGREE);
}
void __cdecl BoilerFX(ITEM_INFO *item) {
PlaySoundEffect(338, NULL, 0);
FlipEffect = -1;
}
void __cdecl FloodFX(ITEM_INFO *item) {
PHD_3DPOS pos;
if (FlipTimer > 120) {
FlipEffect = -1;
} else {
pos.x = LaraItem->pos.x;
if (FlipTimer < 30) {
pos.y = 100 * (30 - FlipTimer) + Camera.target.y;
} else {
pos.y = 100 * (FlipTimer - 30) + Camera.target.y;
}
pos.z = LaraItem->pos.z;
PlaySoundEffect(79, &pos, 0);
}
++FlipTimer;
}
void __cdecl RubbleFX(ITEM_INFO *item) {
PlaySoundEffect(24, NULL, 0);
Camera.bounce = -350;
FlipEffect = -1;
}
void __cdecl ChandelierFX(ITEM_INFO *item) {
PlaySoundEffect(278, NULL, 0);
++FlipTimer;
if (FlipTimer > 30)
FlipEffect = -1;
}
void __cdecl ExplosionFX(ITEM_INFO *item) {
PlaySoundEffect(105, NULL, 0);
Camera.bounce = -75;
FlipEffect = -1;
}
void __cdecl PistonFX(ITEM_INFO *item) {
PlaySoundEffect(190, NULL, 0);
FlipEffect = -1;
}
void __cdecl CurtainFX(ITEM_INFO *item) {
PlaySoundEffect(191, NULL, 0);
FlipEffect = -1;
}
void __cdecl StatueFX(ITEM_INFO *item) {
PlaySoundEffect(331, NULL, 0);
FlipEffect = -1;
}
void __cdecl SetChangeFX(ITEM_INFO *item) {
PlaySoundEffect(330, NULL, 0);
FlipEffect = -1;
}
void __cdecl ControlDingDong(__int16 itemID) {
ITEM_INFO *item;
item = &Items[itemID];
if (CHK_ALL(item->flags, IFL_CODEBITS)) {
PlaySoundEffect(334, &item->pos, 0);
item->flags -= IFL_CODEBITS;
}
}
void __cdecl ControlLaraAlarm(__int16 itemID) {
ITEM_INFO *item;
item = &Items[itemID];
if (CHK_ALL(item->flags, IFL_CODEBITS))
PlaySoundEffect(335, &item->pos, 0);
}
void __cdecl ControlAlarmSound(__int16 itemID) {
ITEM_INFO *item;
int counter;
item = &Items[itemID];
if (CHK_ALL(item->flags, IFL_CODEBITS)) {
PlaySoundEffect(332, &item->pos, 0);
counter = (int) item->data + 1;
if (counter > 6) {
AddDynamicLight(item->pos.x, item->pos.y, item->pos.z, 12, 11);
if (counter > 12)
counter = 0;
}
item->data = (LPVOID) counter;
}
}
void __cdecl ControlBirdTweeter(__int16 itemID) {
ITEM_INFO *item;
item = &Items[itemID];
if (item->objectID == ID_BIRD_TWEETER2) {
if (GetRandomDraw() < 1024)
PlaySoundEffect(316, &item->pos, 0);
} else {
if (GetRandomDraw() < 256)
PlaySoundEffect(329, &item->pos, 0);
}
}
void __cdecl DoChimeSound(ITEM_INFO *item) {
PHD_3DPOS pos;
pos.x = LaraItem->pos.x + ((item->pos.x - LaraItem->pos.x) >> 6);
pos.y = LaraItem->pos.y + ((item->pos.y - LaraItem->pos.y) >> 6);
pos.z = LaraItem->pos.z + ((item->pos.z - LaraItem->pos.z) >> 6);
PlaySoundEffect(208, &pos, 0);
}
void __cdecl ControlClockChimes(__int16 itemID) {
ITEM_INFO *item;
item = &Items[itemID];
if (item->timer) {
if (item->timer % 60 == 59)
DoChimeSound(item);
--item->timer;
if (!item->timer) {
DoChimeSound(item);
item->timer = -1;
RemoveActiveItem(itemID);
item->status = ITEM_INACTIVE;
item->flags &= ~IFL_CODEBITS;
}
}
}
void __cdecl SphereOfDoomCollision(__int16 itemID, ITEM_INFO *laraItem, COLL_INFO *coll) {
ITEM_INFO *item;
int dx, dz, distance, angle;
if (!CHK_ANY(RoomInfo[laraItem->roomNumber].flags, ROOM_UNDERWATER)) {
item = &Items[itemID];
dx = laraItem->pos.x - item->pos.x;
dz = laraItem->pos.z - item->pos.z;
distance = 5 * item->timer >> 1;
if (SQR(dx) + SQR(dz) < SQR(distance)) {
angle = phd_atan(dz, dx);
if (ABS(laraItem->pos.rotY - angle) < PHD_90) {
laraItem->speed = 150;
laraItem->pos.rotY = angle;
} else {
laraItem->speed = -150;
laraItem->pos.rotY = angle + PHD_180;
}
laraItem->gravity = 1;
laraItem->fallSpeed = -50;
laraItem->pos.z = item->pos.z + (phd_cos(angle) * (distance + 50) >> W2V_SHIFT);
laraItem->pos.x = item->pos.x + (phd_sin(angle) * (distance + 50) >> W2V_SHIFT);
laraItem->pos.rotZ = 0;
laraItem->pos.rotX = 0;
LaraItem->animNumber = 34;
LaraItem->frameNumber = Anims[LaraItem->animNumber].frameBase;
LaraItem->currentAnimState = AS_FORWARDJUMP;
LaraItem->goalAnimState = AS_FORWARDJUMP;
}
}
}
void __cdecl SphereOfDoom(__int16 itemID) {
ITEM_INFO *item;
int dx, dy, dz, distance, difference;
PHD_3DPOS pos;
item = &Items[itemID];
item->timer += 64;
item->pos.rotY += item->objectID == ID_SPHERE_OF_DOOM2 ? 10 * PHD_DEGREE : -10 * PHD_DEGREE;
dx = item->pos.x - LaraItem->pos.x;
dy = item->pos.y - LaraItem->pos.y;
dz = item->pos.z - LaraItem->pos.z;
distance = 5 * item->timer >> 1;
difference = phd_sqrt(SQR(dx) + SQR(dy) + SQR(dz)) - distance;
pos.x = LaraItem->pos.x + difference * dx / distance;
pos.y = LaraItem->pos.y + difference * dy / distance;
pos.z = LaraItem->pos.z + difference * dz / distance;
PlaySoundEffect(341, &pos, 0);
if (item->timer > 3840)
KillItem(itemID);
}
void __cdecl DrawSphereOfDoom(ITEM_INFO *item) {
int clip;
phd_PushMatrix();
phd_TranslateAbs(item->pos.x, item->pos.y, item->pos.z);
phd_RotY(item->pos.rotY);
PhdMatrixPtr->_00 = item->timer * PhdMatrixPtr->_00 >> 8;
PhdMatrixPtr->_01 = item->timer * PhdMatrixPtr->_01 >> 8;
PhdMatrixPtr->_02 = item->timer * PhdMatrixPtr->_02 >> 8;
PhdMatrixPtr->_10 = item->timer * PhdMatrixPtr->_10 >> 8;
PhdMatrixPtr->_11 = item->timer * PhdMatrixPtr->_11 >> 8;
PhdMatrixPtr->_12 = item->timer * PhdMatrixPtr->_12 >> 8;
PhdMatrixPtr->_20 = item->timer * PhdMatrixPtr->_20 >> 8;
PhdMatrixPtr->_21 = item->timer * PhdMatrixPtr->_21 >> 8;
PhdMatrixPtr->_22 = item->timer * PhdMatrixPtr->_22 >> 8;
clip = S_GetObjectBounds(Anims[item->animNumber].framePtr);
if (clip) {
CalculateObjectLighting(item, Anims[item->animNumber].framePtr);
phd_PutPolygons(MeshPtr[Objects[item->objectID].meshIndex], clip);
}
phd_PopMatrix();
}
void __cdecl lara_hands_free(ITEM_INFO *item) {
Lara.gun_status = LGS_Armless;
}
void __cdecl flip_map_effect(ITEM_INFO *item) {
FlipMap();
}
void __cdecl draw_right_gun(ITEM_INFO *item) {
__int16 *tmp;
SWAP(Lara.mesh_ptrs[4], MeshPtr[Objects[ID_LARA_PISTOLS].meshIndex + 4], tmp);
SWAP(Lara.mesh_ptrs[10], MeshPtr[Objects[ID_LARA_PISTOLS].meshIndex + 10], tmp);
}
void __cdecl draw_left_gun(ITEM_INFO *item) {
__int16 *tmp;
SWAP(Lara.mesh_ptrs[1], MeshPtr[Objects[ID_LARA_PISTOLS].meshIndex + 1], tmp);
SWAP(Lara.mesh_ptrs[13], MeshPtr[Objects[ID_LARA_PISTOLS].meshIndex + 13], tmp);
}
void __cdecl swap_meshes_with_meshswap1(ITEM_INFO *item) {
int i;
__int16 *tmp;
for (i = 0; i < Objects[item->objectID].nMeshes; ++i)
SWAP(MeshPtr[Objects[item->objectID].meshIndex + i], MeshPtr[Objects[ID_MESH_SWAP1].meshIndex + i], tmp);
}
void __cdecl swap_meshes_with_meshswap2(ITEM_INFO *item) {
int i;
__int16 *tmp;
for (i = 0; i < Objects[item->objectID].nMeshes; ++i)
SWAP(MeshPtr[Objects[item->objectID].meshIndex + i], MeshPtr[Objects[ID_MESH_SWAP2].meshIndex + i], tmp);
}
void __cdecl swap_meshes_with_meshswap3(ITEM_INFO *item) {
int i;
__int16 *tmp;
for (i = 0; i < Objects[item->objectID].nMeshes; ++i) {
if (item == LaraItem)
Lara.mesh_ptrs[i] = MeshPtr[Objects[ID_LARA_SWAP].meshIndex + i];
SWAP(MeshPtr[Objects[item->objectID].meshIndex + i], MeshPtr[Objects[ID_LARA_SWAP].meshIndex + i], tmp);
}
}
void __cdecl invisibility_on(ITEM_INFO *item) {
item->status = ITEM_INVISIBLE;
}
void __cdecl invisibility_off(ITEM_INFO *item) {
item->status = ITEM_ACTIVE;
}
void __cdecl dynamic_light_on(ITEM_INFO *item) {
item->dynamic_light = 1;
}
void __cdecl dynamic_light_off(ITEM_INFO *item) {
item->dynamic_light = 0;
}
void __cdecl reset_hair(ITEM_INFO *item) {
InitialiseHair();
}
void __cdecl AssaultStart(ITEM_INFO *item) {
SaveGame.statistics.timer = 0;
IsAssaultTimerActive = TRUE;
IsAssaultTimerDisplay = TRUE;
FlipEffect = -1;
}
void __cdecl AssaultStop(ITEM_INFO *item) {
IsAssaultTimerActive = FALSE;
IsAssaultTimerDisplay = TRUE;
FlipEffect = -1;
}
void __cdecl AssaultReset(ITEM_INFO *item) {
IsAssaultTimerActive = FALSE;
IsAssaultTimerDisplay = FALSE;
FlipEffect = -1;
}
void __cdecl AssaultFinished(ITEM_INFO *item) {
if (IsAssaultTimerActive) {
AddAssaultTime(SaveGame.statistics.timer);
if (AssaultBestTime < 0) {
if (SaveGame.statistics.timer < 3000) {
S_CDPlay(22, FALSE);
AssaultBestTime = SaveGame.statistics.timer;
} else {
S_CDPlay(24, FALSE);
AssaultBestTime = 3000;
}
} else {
if (SaveGame.statistics.timer < (DWORD) AssaultBestTime) {
S_CDPlay(22, FALSE);
AssaultBestTime = SaveGame.statistics.timer;
} else {
S_CDPlay(SaveGame.statistics.timer >= (DWORD) AssaultBestTime + 150 ? 21 : 23, FALSE);
}
}
IsAssaultTimerActive = FALSE;
}
FlipEffect = -1;
}
/*
* Inject function
*/
void Inject_Effects() {
INJECT(0x0041C4B0, ItemNearLara);
INJECT(0x0041C540, SoundEffects);
INJECT(0x0041C5B0, DoBloodSplat);
INJECT(0x0041C610, DoLotsOfBlood);
INJECT(0x0041C6C0, ControlBlood1);
INJECT(0x0041C750, ControlExplosion1);
INJECT(0x0041C7D0, Richochet);
INJECT(0x0041C850, ControlRichochet1);
INJECT(0x0041C880, CreateBubble);
INJECT(0x0041C8F0, LaraBubbles);
INJECT(0x0041C970, ControlBubble1);
INJECT(0x0041CA70, Splash);
// INJECT(----------, WadeSplash);
INJECT(0x0041CB40, ControlSplash1);
INJECT(0x0041CBC0, ControlWaterSprite);
INJECT(0x0041CC70, ControlSnowSprite);
INJECT(0x0041CD00, ControlHotLiquid);
INJECT(0x0041CDE0, WaterFall);
INJECT(0x0041CF20, finish_level_effect);
INJECT(0x0041CF30, turn180_effect);
INJECT(0x0041CF50, floor_shake_effect);
INJECT(0x0041CFF0, lara_normal_effect);
INJECT(0x0041D030, BoilerFX);
INJECT(0x0041D050, FloodFX);
INJECT(0x0041D0E0, RubbleFX);
INJECT(0x0041D110, ChandelierFX);
INJECT(0x0041D140, ExplosionFX);
INJECT(0x0041D170, PistonFX);
INJECT(0x0041D190, CurtainFX);
INJECT(0x0041D1B0, StatueFX);
INJECT(0x0041D1D0, SetChangeFX);
INJECT(0x0041D1F0, ControlDingDong);
INJECT(0x0041D230, ControlLaraAlarm);
INJECT(0x0041D270, ControlAlarmSound);
INJECT(0x0041D2E0, ControlBirdTweeter);
INJECT(0x0041D340, DoChimeSound);
INJECT(0x0041D3A0, ControlClockChimes);
INJECT(0x0041D410, SphereOfDoomCollision);
INJECT(0x0041D540, SphereOfDoom);
INJECT(0x0041D630, DrawSphereOfDoom);
INJECT(0x0041D760, lara_hands_free);
INJECT(0x0041D770, flip_map_effect);
INJECT(0x0041D780, draw_right_gun);
INJECT(0x0041D7D0, draw_left_gun);
// INJECT(----------, shoot_right_gun);
// INJECT(----------, shoot_left_gun);
INJECT(0x0041D820, swap_meshes_with_meshswap1);
INJECT(0x0041D890, swap_meshes_with_meshswap2);
INJECT(0x0041D900, swap_meshes_with_meshswap3);
INJECT(0x0041D9A0, invisibility_on);
INJECT(0x0041D9B0, invisibility_off);
INJECT(0x0041D9D0, dynamic_light_on);
INJECT(0x0041D9E0, dynamic_light_off);
INJECT(0x0041D9F0, reset_hair);
INJECT(0x0041DA00, AssaultStart);
INJECT(0x0041DA30, AssaultStop);
INJECT(0x0041DA50, AssaultReset);
INJECT(0x0041DA70, AssaultFinished);
}
================================================
FILE: game/effects.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef EFFECTS_H_INCLUDED
#define EFFECTS_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
int __cdecl ItemNearLara(PHD_3DPOS *pos, int distance); // 0x0041C4B0
void __cdecl SoundEffects(); // 0x0041C540
__int16 __cdecl DoBloodSplat(int x, int y, int z, __int16 speed, __int16 direction, __int16 roomID); // 0x0041C5B0
void __cdecl DoLotsOfBlood(int x, int y, int z, __int16 speed, __int16 direction, __int16 roomID, int number); // 0x0041C610
void __cdecl ControlBlood1(__int16 fxID); // 0x0041C6C0
void __cdecl ControlExplosion1(__int16 fxID); // 0x0041C750
void __cdecl Richochet(GAME_VECTOR *pos); // 0x0041C7D0
void __cdecl ControlRichochet1(__int16 fxID); // 0x0041C850
void __cdecl CreateBubble(PHD_3DPOS *pos, __int16 roomNumber); // 0x0041C880
void __cdecl LaraBubbles(ITEM_INFO *item); // 0x0041C8F0
void __cdecl ControlBubble1(__int16 fxID); // 0x0041C970
void __cdecl Splash(ITEM_INFO *item); // 0x0041CA70
void WadeSplash(ITEM_INFO *item, int height);
void __cdecl ControlSplash1(__int16 fxID); // 0x0041CB40
void __cdecl ControlWaterSprite(__int16 fxID); // 0x0041CBC0
void __cdecl ControlSnowSprite(__int16 fxID); // 0x0041CC70
void __cdecl ControlHotLiquid(__int16 fxID); // 0x0041CD00
void __cdecl WaterFall(__int16 itemID); // 0x0041CDE0
void __cdecl finish_level_effect(ITEM_INFO *item); // 0x0041CF20
void __cdecl turn180_effect(ITEM_INFO *item); // 0x0041CF30
void __cdecl floor_shake_effect(ITEM_INFO *item); // 0x0041CF50
void __cdecl lara_normal_effect(ITEM_INFO *item); // 0x0041CFF0
void __cdecl BoilerFX(ITEM_INFO *item); // 0x0041D030
void __cdecl FloodFX(ITEM_INFO *item); // 0x0041D050
void __cdecl RubbleFX(ITEM_INFO *item); // 0x0041D0E0
void __cdecl ChandelierFX(ITEM_INFO *item); // 0x0041D110
void __cdecl ExplosionFX(ITEM_INFO *item); // 0x0041D140
void __cdecl PistonFX(ITEM_INFO *item); // 0x0041D170
void __cdecl CurtainFX(ITEM_INFO *item); // 0x0041D190
void __cdecl StatueFX(ITEM_INFO *item); // 0x0041D1B0
void __cdecl SetChangeFX(ITEM_INFO *item); // 0x0041D1D0
void __cdecl ControlDingDong(__int16 itemID); // 0x0041D1F0
void __cdecl ControlLaraAlarm(__int16 itemID); // 0x0041D230
void __cdecl ControlAlarmSound(__int16 itemID); // 0x0041D270
void __cdecl ControlBirdTweeter(__int16 itemID); // 0x0041D2E0
void __cdecl DoChimeSound(ITEM_INFO *item); // 0x0041D340
void __cdecl ControlClockChimes(__int16 itemID); // 0x0041D3A0
void __cdecl SphereOfDoomCollision(__int16 itemID, ITEM_INFO *laraItem, COLL_INFO *coll); // 0x0041D410
void __cdecl SphereOfDoom(__int16 itemID); // 0x0041D540
void __cdecl DrawSphereOfDoom(ITEM_INFO *item); // 0x0041D630
void __cdecl lara_hands_free(ITEM_INFO *item); // 0x0041D760
void __cdecl flip_map_effect(ITEM_INFO *item); // 0x0041D770
void __cdecl draw_right_gun(ITEM_INFO *item); // 0x0041D780
void __cdecl draw_left_gun(ITEM_INFO *item); // 0x0041D7D0
// ----------: shoot_right_gun
// ----------: shoot_left_gun
void __cdecl swap_meshes_with_meshswap1(ITEM_INFO *item); // 0x0041D820
void __cdecl swap_meshes_with_meshswap2(ITEM_INFO *item); // 0x0041D890
void __cdecl swap_meshes_with_meshswap3(ITEM_INFO *item); // 0x0041D900
void __cdecl invisibility_on(ITEM_INFO *item); // 0x0041D9A0
void __cdecl invisibility_off(ITEM_INFO *item); // 0x0041D9B0
void __cdecl dynamic_light_on(ITEM_INFO *item); // 0x0041D9D0
void __cdecl dynamic_light_off(ITEM_INFO *item); // 0x0041D9E0
void __cdecl reset_hair(ITEM_INFO *item); // 0x0041D9F0
void __cdecl AssaultStart(ITEM_INFO *item); // 0x0041DA00
void __cdecl AssaultStop(ITEM_INFO *item); // 0x0041DA30
void __cdecl AssaultReset(ITEM_INFO *item); // 0x0041DA50
void __cdecl AssaultFinished(ITEM_INFO *item); // 0x0041DA70
#endif // EFFECTS_H_INCLUDED
================================================
FILE: game/enemies.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/enemies.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Enemies() {
// INJECT(0x0041DB30, Knife);
// INJECT(0x0041DBB0, Cult2Control);
// INJECT(0x0041DFE0, MonkControl);
// INJECT(0x0041E4B0, Worker3Control);
// INJECT(0x0041EAC0, DrawXianLord);
// INJECT(0x0041EEC0, XianDamage);
// INJECT(0x0041EF70, InitialiseXianLord);
// INJECT(0x0041EFD0, XianLordControl);
// INJECT(0x0041F5B0, WarriorSparkleTrail);
// INJECT(0x0041F650, WarriorControl);
}
================================================
FILE: game/enemies.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef ENEMIES_H_INCLUDED
#define ENEMIES_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x0041DB30: Knife
#define Cult2Control ((void(__cdecl*)(__int16)) 0x0041DBB0)
#define MonkControl ((void(__cdecl*)(__int16)) 0x0041DFE0)
#define Worker3Control ((void(__cdecl*)(__int16)) 0x0041E4B0)
#define DrawXianLord ((void(__cdecl*)(ITEM_INFO *)) 0x0041EAC0)
// 0x0041EEC0: XianDamage
#define InitialiseXianLord ((void(__cdecl*)(__int16)) 0x0041EF70)
#define XianLordControl ((void(__cdecl*)(__int16)) 0x0041EFD0)
// 0x0041F5B0: WarriorSparkleTrail
#define WarriorControl ((void(__cdecl*)(__int16)) 0x0041F650)
#endif // ENEMIES_H_INCLUDED
================================================
FILE: game/gameflow.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/cinema.h"
#include "game/demo.h"
#include "game/gameflow.h"
#include "game/health.h"
#include "game/invfunc.h"
#include "game/invtext.h"
#include "specific/file.h"
#include "specific/frontend.h"
#include "specific/game.h"
#include "specific/output.h"
#include "global/vars.h"
#ifdef FEATURE_BACKGROUND_IMPROVED
#include "modding/background_new.h"
bool LoadingScreensEnabled = true;
#endif // FEATURE_BACKGROUND_IMPROVED
static int CurrentEvent = GFE_END_SEQ; // NOTE: not presented in the original game
// NOTE: there is no such function in the original code
static bool GF_GetSequenceValue(DWORD levelID, GF_EVENTS event, __int16 *pValue, __int16 defValue) {
if( levelID >= GF_GameFlow.num_Levels ) {
return false;
}
__int16 *seq = GF_ScriptTable[levelID];
__int16 operand = 0;
bool result = false;
if( pValue != NULL) {
*pValue = defValue; // set default value just in case
}
while( *seq != GFE_END_SEQ ) {
__int16 seqCode = *seq;
switch( seqCode ) {
case GFE_STARTLEVEL :
case GFE_LOADINGPIC :
case GFE_DEMOPLAY :
case GFE_CUTANGLE :
case GFE_CUTSCENE :
case GFE_PLAYFMV :
case GFE_PICTURE :
case GFE_JUMPTO_SEQ :
case GFE_SETTRACK :
case GFE_NOFLOOR :
case GFE_STARTANIM :
case GFE_NUMSECRETS :
case GFE_ADD2INV :
operand = seq[1];
seq += 2;
break;
case GFE_LEVCOMPLETE :
case GFE_GAMECOMPLETE :
case GFE_SUNSET :
case GFE_DEADLY_WATER :
case GFE_REMOVE_WEAPONS :
case GFE_REMOVE_AMMO :
case GFE_KILL2COMPLETE :
case GFE_LIST_START :
case GFE_LIST_END :
operand = 0;
++seq;
break;
default :
return result;
}
if( seqCode == event ) {
// the event is found in the sequence
if( pValue == NULL) {
// if we don't need operand value, just return here
return true;
} else {
// if we need the value, search until the end, the last value is correct one
result = true;
*pValue = operand;
}
}
}
return result;
}
// NOTE: there is no such function in the original code
int GF_GetNumSecrets(DWORD levelID) {
__int16 result = 3;
GF_GetSequenceValue(levelID, GFE_NUMSECRETS, &result, result);
return result;
}
// NOTE: there is no such function in the original code
bool GF_IsFinalLevel(DWORD levelID) {
return GF_GetSequenceValue(levelID, GFE_GAMECOMPLETE, NULL, 0);
}
BOOL __cdecl GF_LoadScriptFile(LPCTSTR fileName) {
GF_SunsetEnabled = 0;
if( !S_LoadGameFlow(fileName) )
return FALSE;
GF_GameFlow.levelCompleteTrack = 41; // "level complete" track is hardcoded for some reason
InvCompassOption.lpString = GF_GameStringTable[GSI_InvItem_Statistics];
InvPistolOption.lpString = GF_GameStringTable[GSI_InvItem_Pistols];
InvShotgunOption.lpString = GF_GameStringTable[GSI_InvItem_Shotgun];
InvMagnumOption.lpString = GF_GameStringTable[GSI_InvItem_Magnums];
InvUziOption.lpString = GF_GameStringTable[GSI_InvItem_Uzis];
InvHarpoonOption.lpString = GF_GameStringTable[GSI_InvItem_Harpoon];
InvM16Option.lpString = GF_GameStringTable[GSI_InvItem_M16];
InvGrenadeOption.lpString = GF_GameStringTable[GSI_InvItem_Grenade];
InvFlareOption.lpString = GF_GameStringTable[GSI_InvItem_Flare];
InvPistolAmmoOption.lpString = GF_GameStringTable[GSI_InvItem_PistolAmmo];
InvShotgunAmmoOption.lpString = GF_GameStringTable[GSI_InvItem_ShotgunAmmo];
InvMagnumAmmoOption.lpString = GF_GameStringTable[GSI_InvItem_MagnumAmmo];
InvUziAmmoOption.lpString = GF_GameStringTable[GSI_InvItem_UziAmmo];
InvHarpoonAmmoOption.lpString = GF_GameStringTable[GSI_InvItem_HarpoonAmmo];
InvM16AmmoOption.lpString = GF_GameStringTable[GSI_InvItem_M16Ammo];
InvGrenadeAmmoOption.lpString = GF_GameStringTable[GSI_InvItem_GrenadeAmmo];
InvSmallMedipackOption.lpString = GF_GameStringTable[GSI_InvItem_SmallMedipack];
InvLargeMedipackOption.lpString = GF_GameStringTable[GSI_InvItem_LargeMedipack];
InvPickup1Option.lpString = GF_GameStringTable[GSI_InvItem_Pickup];
InvPickup2Option.lpString = GF_GameStringTable[GSI_InvItem_Pickup];
InvPuzzle1Option.lpString = GF_GameStringTable[GSI_InvItem_Puzzle];
InvPuzzle2Option.lpString = GF_GameStringTable[GSI_InvItem_Puzzle];
InvPuzzle3Option.lpString = GF_GameStringTable[GSI_InvItem_Puzzle];
InvPuzzle4Option.lpString = GF_GameStringTable[GSI_InvItem_Puzzle];
InvKey1Option.lpString = GF_GameStringTable[GSI_InvItem_Key];
InvKey2Option.lpString = GF_GameStringTable[GSI_InvItem_Key];
InvKey3Option.lpString = GF_GameStringTable[GSI_InvItem_Key];
InvKey4Option.lpString = GF_GameStringTable[GSI_InvItem_Key];
InvPassportOption.lpString = GF_GameStringTable[GSI_InvItem_Game];
InvPhotoOption.lpString = GF_GameStringTable[GSI_InvItem_LaraHome];
InvDetailOption.lpString = GF_SpecificStringTable[SSI_DetailLevels];
InvSoundOption.lpString = GF_SpecificStringTable[SSI_Sound];
InvControlOption.lpString = GF_SpecificStringTable[SSI_Controls];
SetRequesterHeading(&LoadGameRequester, GF_GameStringTable[GSI_Passport_SelectLevel], 0, NULL, 0);
SetRequesterHeading(&SaveGameRequester, GF_GameStringTable[GSI_Passport_SelectLevel], 0, NULL, 0);
return TRUE;
}
BOOL __cdecl GF_DoFrontEndSequence() {
return ( GF_EXIT_GAME == GF_InterpretSequence(GF_ScriptBuffer, GFL_NORMAL, 1) );
}
int __cdecl GF_DoLevelSequence(DWORD levelID, GF_LEVEL_TYPE levelType) {
for( DWORD i = levelID; i < GF_GameFlow.num_Levels; ++i ) {
int direction = GF_InterpretSequence(GF_ScriptTable[i], levelType, 0);
if( GF_GameFlow.singleLevel >= 0 ||
(direction & ~0xFFu) != GF_LEVEL_COMPLETE )
{
return direction;
}
}
IsTitleLoaded = FALSE;
return GF_EXIT_TO_TITLE;
}
int __cdecl GF_InterpretSequence(__int16 *seq, GF_LEVEL_TYPE levelType, int seqType) {
int result = GF_EXIT_TO_TITLE;
int trackIndex = 0;
char str[80];
GF_NoFloor = 0;
GF_DeadlyWater = 0;
GF_SunsetEnabled = 0;
GF_LaraStartAnim = 0;
GF_Kill2Complete = 0;
GF_RemoveAmmo = 0;
GF_RemoveWeapons = 0;
memset(GF_Add2InvItems, 0, sizeof(GF_Add2InvItems));
memset(GF_SecretInvItems, 0, sizeof(GF_SecretInvItems));
TrackIDs[0] = 2;
CineTargetAngle = 0x4000;
GF_NumSecrets = 3;
while( *seq != GFE_END_SEQ ) {
CurrentEvent = *seq;
switch( *seq ) {
case GFE_STARTLEVEL :
if( seq[1] > GF_GameFlow.num_Levels ) {
sprintf(str, "INVALID LEVEL %d", seq[1]);
result = GF_EXIT_TO_TITLE;
} else if( levelType != GFL_STORY ) {
if( levelType == GFL_MIDSTORY ) {
return GF_EXIT_TO_TITLE;
}
result = StartGame(seq[1], levelType);
GF_StartGame = false;
if( levelType == GFL_SAVED ) {
levelType = GFL_NORMAL;
}
if( (result & ~0xff) != GF_LEVEL_COMPLETE ) {
return result;
}
}
seq += 2;
break;
case GFE_LOADINGPIC :
seq += 2;
break;
case GFE_DEMOPLAY :
if( levelType != GFL_SAVED && levelType != GFL_STORY && levelType != GFL_MIDSTORY ) {
return StartDemo(seq[1]);
}
seq += 2;
break;
case GFE_CUTANGLE :
if( levelType != GFL_SAVED ) {
CineTargetAngle = seq[1];
}
seq += 2;
break;
case GFE_CUTSCENE :
if( levelType != GFL_SAVED ) {
sprintf(str, "CUTSCENE %d %s", seq[1], GF_CutsFilesStringTable[seq[1]]);
__int16 storedLevel = CurrentLevel;
int cine_ret = StartCinematic(seq[1]);
CurrentLevel = storedLevel;
if( cine_ret == 2 && (levelType == GFL_STORY || levelType == GFL_MIDSTORY) ) {
return GF_EXIT_TO_TITLE;
}
if( cine_ret == 3 ) {
return GF_EXIT_GAME;
}
}
seq += 2;
break;
case GFE_PLAYFMV :
if( levelType != GFL_SAVED ) {
if( seq[2] == GFE_PLAYFMV ) {
if( S_IntroFMV(GF_FmvFilesStringTable[seq[1]], GF_FmvFilesStringTable[seq[3]]) ) {
return GF_EXIT_GAME;
}
seq += 2;
} else {
if( S_PlayFMV(GF_FmvFilesStringTable[seq[1]]) ) {
return GF_EXIT_GAME;
}
}
}
seq += 2;
break;
case GFE_LEVCOMPLETE :
if( levelType != GFL_STORY && levelType != GFL_MIDSTORY ) {
if( LevelStats(CurrentLevel) ) {
return GF_EXIT_TO_TITLE;
}
result = GF_START_GAME | (CurrentLevel + 1);
}
++seq;
break;
case GFE_GAMECOMPLETE :
DisplayCredits();
if( GameStats(CurrentLevel) ) {
return GF_EXIT_TO_TITLE;
}
result = GF_EXIT_TO_TITLE;
++seq;
break;
case GFE_PICTURE :
if( levelType != GFL_SAVED ) {
sprintf(str, "PICTURE %s", GF_PictureFilesStringTable[seq[1]]);
}
#ifdef FEATURE_BACKGROUND_IMPROVED
if( LoadingScreensEnabled && seq[1] < GF_GameFlow.num_Pictures && (levelType == GFL_NORMAL || levelType == GFL_SAVED) ) {
RGB888 palette[256];
memcpy(palette, GamePalette8, sizeof(GamePalette8));
if( !BGND2_LoadPicture(GF_PictureFilesStringTable[seq[1]], FALSE, FALSE) ) {
BGND2_ShowPicture(30, 90, 10, 2, TRUE);
S_DontDisplayPicture();
}
memcpy(GamePalette8, palette, sizeof(GamePalette8));
}
#endif // FEATURE_BACKGROUND_IMPROVED
seq += 2;
break;
case GFE_JUMPTO_SEQ :
sprintf(str, "JUMPSEQ %d", seq[1]);
seq += 2;
break;
case GFE_SETTRACK :
TrackIDs[trackIndex++] = seq[1];
SetCutsceneTrack(seq[1]);
seq += 2;
break;
case GFE_SUNSET :
if( levelType != GFL_STORY && levelType != GFL_MIDSTORY ) {
GF_SunsetEnabled = 1;
}
++seq;
break;
case GFE_DEADLY_WATER :
if( levelType != GFL_STORY && levelType != GFL_MIDSTORY ) {
GF_DeadlyWater = 1;
}
++seq;
break;
case GFE_NOFLOOR :
if( levelType != GFL_STORY && levelType != GFL_MIDSTORY ) {
GF_NoFloor = seq[1];
}
seq += 2;
break;
case GFE_STARTANIM :
if( levelType != GFL_STORY && levelType != GFL_MIDSTORY ) {
GF_LaraStartAnim = seq[1];
}
seq += 2;
break;
case GFE_NUMSECRETS :
if( levelType != GFL_STORY && levelType != GFL_MIDSTORY ) {
GF_NumSecrets = seq[1];
}
seq += 2;
break;
case GFE_ADD2INV :
if( levelType != GFL_STORY && levelType != GFL_MIDSTORY ) {
if( seq[1] < 1000 ) {
++GF_SecretInvItems[seq[1]];
} else if( levelType != GFL_SAVED ) {
++GF_Add2InvItems[seq[1] - 1000];
}
}
seq += 2;
break;
case GFE_REMOVE_WEAPONS :
if( levelType != GFL_STORY && levelType != GFL_MIDSTORY && levelType != GFL_SAVED ) {
GF_RemoveWeapons = 1;
}
++seq;
break;
case GFE_REMOVE_AMMO :
if( levelType != GFL_STORY && levelType != GFL_MIDSTORY && levelType != GFL_SAVED ) {
GF_RemoveAmmo = 1;
}
++seq;
break;
case GFE_KILL2COMPLETE :
if( levelType != GFL_STORY && levelType != GFL_MIDSTORY ) {
GF_Kill2Complete = 1;
}
++seq;
break;
case GFE_LIST_START :
case GFE_LIST_END :
++seq;
break;
default :
return GF_EXIT_GAME;
}
}
CurrentEvent = GFE_END_SEQ;
if( levelType == GFL_STORY || levelType == GFL_MIDSTORY ) {
result = GF_START_GAME;
}
return result;
}
void __cdecl GF_ModifyInventory(int levelID, BOOL isSecret) {
int i;
START_INFO *start = &SaveGame.start[levelID];
// NOTE: additional weapon availability checks not presented in the original game
if( !Objects[ID_PISTOL_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_PISTOL] = 0;
GF_SecretInvItems[ADDINV_PISTOL] = 0;
}
if( !Objects[ID_UZI_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_UZI] = 0;
GF_SecretInvItems[ADDINV_UZI] = 0;
}
if( !Objects[ID_MAGNUM_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_MAGNUM] = 0;
GF_SecretInvItems[ADDINV_MAGNUM] = 0;
}
if( !Objects[ID_SHOTGUN_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_SHOTGUN] = 0;
GF_SecretInvItems[ADDINV_SHOTGUN] = 0;
}
if( !Objects[ID_GRENADE_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_GRENADE] = 0;
GF_SecretInvItems[ADDINV_GRENADE] = 0;
}
if( !Objects[ID_M16_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_M16] = 0;
GF_SecretInvItems[ADDINV_M16] = 0;
}
if( !Objects[ID_HARPOON_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_HARPOON] = 0;
GF_SecretInvItems[ADDINV_HARPOON] = 0;
}
// NOTE: additional ammo availability checks not presented in the original game
if( !start->has_pistols && !Objects[ID_PISTOL_AMMO_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_PISTOL_AMMO] = 0;
GF_SecretInvItems[ADDINV_PISTOL_AMMO] = 0;
}
if( !start->has_uzis && !Objects[ID_UZI_AMMO_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_UZI_AMMO] = 0;
GF_SecretInvItems[ADDINV_UZI_AMMO] = 0;
}
if( !start->has_magnums && !Objects[ID_MAGNUM_AMMO_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_MAGNUM_AMMO] = 0;
GF_SecretInvItems[ADDINV_MAGNUM_AMMO] = 0;
}
if( !start->has_shotgun && !Objects[ID_SHOTGUN_AMMO_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_SHOTGUN_AMMO] = 0;
GF_SecretInvItems[ADDINV_SHOTGUN_AMMO] = 0;
}
if( !start->has_grenade && !Objects[ID_GRENADE_AMMO_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_GRENADE_AMMO] = 0;
GF_SecretInvItems[ADDINV_GRENADE_AMMO] = 0;
}
if( !start->has_m16 && !Objects[ID_M16_AMMO_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_M16_AMMO] = 0;
GF_SecretInvItems[ADDINV_M16_AMMO] = 0;
}
if( !start->has_harpoon && !Objects[ID_HARPOON_AMMO_OPTION].loaded ) {
GF_Add2InvItems[ADDINV_HARPOON_AMMO] = 0;
GF_SecretInvItems[ADDINV_HARPOON_AMMO] = 0;
}
// Pistols
if( !start->has_pistols && GF_Add2InvItems[ADDINV_PISTOL] ) {
start->has_pistols = 1;
Inv_AddItem(ID_PISTOL_ITEM);
}
// Shotgun
if( Inv_RequestItem(ID_SHOTGUN_ITEM) ) {
if( isSecret ) {
Lara.shotgun_ammo += 12 * GF_SecretInvItems[ADDINV_SHOTGUN_AMMO];
for( i = 0; i < GF_SecretInvItems[ADDINV_SHOTGUN_AMMO]; ++i ) {
AddDisplayPickup(ID_SHOTGUN_AMMO_ITEM);
}
} else {
Lara.shotgun_ammo += 12 * GF_Add2InvItems[ADDINV_SHOTGUN_AMMO];
}
} else if( (!isSecret && GF_Add2InvItems[ADDINV_SHOTGUN]) || (isSecret && GF_SecretInvItems[ADDINV_SHOTGUN]) ) {
start->has_shotgun = 1;
Inv_AddItem(ID_SHOTGUN_ITEM);
if( isSecret ) {
AddDisplayPickup(ID_SHOTGUN_ITEM);
Lara.shotgun_ammo += 12 * GF_SecretInvItems[ADDINV_SHOTGUN_AMMO];
// NOTE: This loop is absent in the original code
for( i = 0; i < GF_SecretInvItems[ADDINV_SHOTGUN_AMMO]; ++i ) {
AddDisplayPickup(ID_SHOTGUN_AMMO_ITEM);
}
} else {
Lara.shotgun_ammo += 12 * GF_Add2InvItems[ADDINV_SHOTGUN_AMMO];
}
} else {
if( isSecret ) {
for( i = 0; i < GF_SecretInvItems[ADDINV_SHOTGUN_AMMO]; ++i ) {
Inv_AddItem(ID_SHOTGUN_AMMO_ITEM);
AddDisplayPickup(ID_SHOTGUN_AMMO_ITEM);
}
} else {
for( i = 0; i < GF_Add2InvItems[ADDINV_SHOTGUN_AMMO]; ++i ) {
Inv_AddItem(ID_SHOTGUN_AMMO_ITEM);
}
}
}
// Magnums
if( Inv_RequestItem(ID_MAGNUM_ITEM) ) {
if( isSecret ) {
Lara.magnum_ammo += 40 * GF_SecretInvItems[ADDINV_MAGNUM_AMMO];
for( i = 0; i < GF_SecretInvItems[ADDINV_MAGNUM_AMMO]; ++i ) {
AddDisplayPickup(ID_MAGNUM_AMMO_ITEM);
}
} else {
Lara.magnum_ammo += 40 * GF_Add2InvItems[ADDINV_MAGNUM_AMMO];
}
} else if( (!isSecret && GF_Add2InvItems[ADDINV_MAGNUM]) || (isSecret && GF_SecretInvItems[ADDINV_MAGNUM]) ) {
start->has_magnums = 1;
Inv_AddItem(ID_MAGNUM_ITEM);
if( isSecret ) {
AddDisplayPickup(ID_MAGNUM_ITEM);
// NOTE: here was GF_Add2InvItems instead of GF_SecretInvItems in the original code. This is fixed
Lara.magnum_ammo += 40 * GF_SecretInvItems[ADDINV_MAGNUM_AMMO];
// NOTE: here was ADDINV_MAGNUM instead of ADDINV_MAGNUM_AMMO in the original code. This is fixed
for( i = 0; i < GF_SecretInvItems[ADDINV_MAGNUM_AMMO]; ++i ) {
AddDisplayPickup(ID_MAGNUM_AMMO_ITEM);
}
} else {
Lara.magnum_ammo += 40 * GF_Add2InvItems[ADDINV_MAGNUM_AMMO];
}
} else {
if( isSecret ) {
for( i = 0; i < GF_SecretInvItems[ADDINV_MAGNUM_AMMO]; ++i ) {
Inv_AddItem(ID_MAGNUM_AMMO_ITEM);
AddDisplayPickup(ID_MAGNUM_AMMO_ITEM);
}
} else {
for( i = 0; i < GF_Add2InvItems[ADDINV_MAGNUM_AMMO]; ++i ) {
Inv_AddItem(ID_MAGNUM_AMMO_ITEM);
}
}
}
// Uzis
if( Inv_RequestItem(ID_UZI_ITEM) ) {
if( isSecret ) {
Lara.uzi_ammo += 80 * GF_SecretInvItems[ADDINV_UZI_AMMO];
for( i = 0; i < GF_SecretInvItems[ADDINV_UZI_AMMO]; ++i ) {
AddDisplayPickup(ID_UZI_AMMO_ITEM);
}
} else {
Lara.uzi_ammo += 80 * GF_Add2InvItems[ADDINV_UZI_AMMO];
}
} else if( (!isSecret && GF_Add2InvItems[ADDINV_UZI]) || (isSecret && GF_SecretInvItems[ADDINV_UZI]) ) {
start->has_uzis = 1;
Inv_AddItem(ID_UZI_ITEM);
if( isSecret ) {
AddDisplayPickup(ID_UZI_ITEM);
Lara.uzi_ammo += 80 * GF_SecretInvItems[ADDINV_UZI_AMMO];
for( i = 0; i < GF_SecretInvItems[ADDINV_UZI_AMMO]; ++i ) {
AddDisplayPickup(ID_UZI_AMMO_ITEM);
}
} else {
Lara.uzi_ammo += 80 * GF_Add2InvItems[ADDINV_UZI_AMMO];
}
} else {
if( isSecret ) {
for( i = 0; i < GF_SecretInvItems[ADDINV_UZI_AMMO]; ++i ) {
Inv_AddItem(ID_UZI_AMMO_ITEM);
AddDisplayPickup(ID_UZI_AMMO_ITEM);
}
} else {
for( i = 0; i < GF_Add2InvItems[ADDINV_UZI_AMMO]; ++i ) {
Inv_AddItem(ID_UZI_AMMO_ITEM);
}
}
}
// Harpoon
if( Inv_RequestItem(ID_HARPOON_ITEM) ) {
if( isSecret ) {
Lara.harpoon_ammo += 3 * GF_SecretInvItems[ADDINV_HARPOON_AMMO];
for( i = 0; i < GF_SecretInvItems[ADDINV_HARPOON_AMMO]; ++i ) {
AddDisplayPickup(ID_HARPOON_AMMO_ITEM);
}
} else {
Lara.harpoon_ammo += 3 * GF_Add2InvItems[ADDINV_HARPOON_AMMO];
}
} else if( (!isSecret && GF_Add2InvItems[ADDINV_HARPOON]) || (isSecret && GF_SecretInvItems[ADDINV_HARPOON]) ) {
start->has_harpoon = 1;
Inv_AddItem(ID_HARPOON_ITEM);
if( isSecret ) {
AddDisplayPickup(ID_HARPOON_ITEM);
Lara.harpoon_ammo += 3 * GF_SecretInvItems[ADDINV_HARPOON_AMMO];
for( i = 0; i < GF_SecretInvItems[ADDINV_HARPOON_AMMO]; ++i ) {
AddDisplayPickup(ID_HARPOON_AMMO_ITEM);
}
} else {
Lara.harpoon_ammo += 3 * GF_Add2InvItems[ADDINV_HARPOON_AMMO];
}
} else {
if( isSecret ) {
for( i = 0; i < GF_SecretInvItems[ADDINV_HARPOON_AMMO]; ++i ) {
Inv_AddItem(ID_HARPOON_AMMO_ITEM);
AddDisplayPickup(ID_HARPOON_AMMO_ITEM);
}
} else {
for( i = 0; i < GF_Add2InvItems[ADDINV_HARPOON_AMMO]; ++i ) {
Inv_AddItem(ID_HARPOON_AMMO_ITEM);
}
}
}
// M16
if( Inv_RequestItem(ID_M16_ITEM) ) {
if( isSecret ) {
Lara.m16_ammo += 40 * GF_SecretInvItems[ADDINV_M16_AMMO];
for( i = 0; i < GF_SecretInvItems[ADDINV_M16_AMMO]; ++i ) {
AddDisplayPickup(ID_M16_AMMO_ITEM);
}
} else {
Lara.m16_ammo += 40 * GF_Add2InvItems[ADDINV_M16_AMMO];
}
} else if( (!isSecret && GF_Add2InvItems[ADDINV_M16]) || (isSecret && GF_SecretInvItems[ADDINV_M16]) ) {
start->has_m16 = 1;
Inv_AddItem(ID_M16_ITEM);
if( isSecret ) {
AddDisplayPickup(ID_M16_ITEM);
Lara.m16_ammo += 40 * GF_SecretInvItems[ADDINV_M16_AMMO];
for( i = 0; i < GF_SecretInvItems[ADDINV_M16_AMMO]; ++i ) {
AddDisplayPickup(ID_M16_AMMO_ITEM);
}
} else {
Lara.m16_ammo += 40 * GF_Add2InvItems[ADDINV_M16_AMMO];
}
} else {
if( isSecret ) {
for( i = 0; i < GF_SecretInvItems[ADDINV_M16_AMMO]; ++i ) {
Inv_AddItem(ID_M16_AMMO_ITEM);
AddDisplayPickup(ID_M16_AMMO_ITEM);
}
} else {
for( i = 0; i < GF_Add2InvItems[ADDINV_M16_AMMO]; ++i ) {
Inv_AddItem(ID_M16_AMMO_ITEM);
}
}
}
// Grenade
if( Inv_RequestItem(ID_GRENADE_ITEM) ) {
if( isSecret ) {
Lara.grenade_ammo += 2 * GF_SecretInvItems[ADDINV_GRENADE_AMMO];
for( i = 0; i < GF_SecretInvItems[ADDINV_GRENADE_AMMO]; ++i ) {
AddDisplayPickup(ID_GRENADE_AMMO_ITEM);
}
} else {
Lara.grenade_ammo += 2 * GF_Add2InvItems[ADDINV_GRENADE_AMMO];
}
} else if( (!isSecret && GF_Add2InvItems[ADDINV_GRENADE]) || (isSecret && GF_SecretInvItems[ADDINV_GRENADE]) ) {
start->has_grenade = 1;
Inv_AddItem(ID_GRENADE_ITEM);
if( isSecret ) {
AddDisplayPickup(ID_GRENADE_ITEM);
Lara.grenade_ammo += 2 * GF_SecretInvItems[ADDINV_GRENADE_AMMO];
for( i = 0; i < GF_SecretInvItems[ADDINV_GRENADE_AMMO]; ++i ) {
AddDisplayPickup(ID_GRENADE_AMMO_ITEM);
}
} else {
Lara.grenade_ammo += 2 * GF_Add2InvItems[ADDINV_GRENADE_AMMO];
}
} else {
if( isSecret ) {
for( i = 0; i < GF_SecretInvItems[ADDINV_GRENADE_AMMO]; ++i ) {
Inv_AddItem(ID_GRENADE_AMMO_ITEM);
AddDisplayPickup(ID_GRENADE_AMMO_ITEM);
}
} else {
for( i = 0; i < GF_Add2InvItems[ADDINV_GRENADE_AMMO]; ++i ) {
Inv_AddItem(ID_GRENADE_AMMO_ITEM);
}
}
}
if( isSecret ) {
// Flares (secret bonus)
for( i = 0; i < GF_SecretInvItems[ADDINV_FLARE]; ++i ) {
Inv_AddItem(ID_FLARE_ITEM);
AddDisplayPickup(ID_FLARE_ITEM);
}
// Medipacks (secret bonus)
for( i = 0; i < GF_SecretInvItems[ADDINV_SMALL_MEDIPACK]; ++i ) {
Inv_AddItem(ID_SMALL_MEDIPACK_ITEM);
AddDisplayPickup(ID_SMALL_MEDIPACK_ITEM);
}
for( i = 0; i < GF_SecretInvItems[ADDINV_LARGE_MEDIPACK]; ++i ) {
Inv_AddItem(ID_LARGE_MEDIPACK_ITEM);
AddDisplayPickup(ID_LARGE_MEDIPACK_ITEM);
}
// Pickups (secret bonus)
for( i = 0; i < GF_SecretInvItems[ADDINV_PICKUP1]; ++i ) {
Inv_AddItem(ID_PICKUP_ITEM1);
AddDisplayPickup(ID_PICKUP_ITEM1);
}
for( i = 0; i < GF_SecretInvItems[ADDINV_PICKUP2]; ++i ) {
Inv_AddItem(ID_PICKUP_ITEM2);
AddDisplayPickup(ID_PICKUP_ITEM2);
}
// Puzzles (secret bonus)
for( i = 0; i < GF_SecretInvItems[ADDINV_PUZZLE1]; ++i ) {
Inv_AddItem(ID_PUZZLE_ITEM1);
AddDisplayPickup(ID_PUZZLE_ITEM1);
}
for( i = 0; i < GF_SecretInvItems[ADDINV_PUZZLE2]; ++i ) {
Inv_AddItem(ID_PUZZLE_ITEM2);
AddDisplayPickup(ID_PUZZLE_ITEM2);
}
for( i = 0; i < GF_SecretInvItems[ADDINV_PUZZLE3]; ++i ) {
Inv_AddItem(ID_PUZZLE_ITEM3);
AddDisplayPickup(ID_PUZZLE_ITEM3);
}
for( i = 0; i < GF_SecretInvItems[ADDINV_PUZZLE4]; ++i ) {
Inv_AddItem(ID_PUZZLE_ITEM4);
AddDisplayPickup(ID_PUZZLE_ITEM4);
}
// Keys (secret bonus)
for( i = 0; i < GF_SecretInvItems[ADDINV_KEY1]; ++i ) {
Inv_AddItem(ID_KEY_ITEM1);
AddDisplayPickup(ID_KEY_ITEM1);
}
for( i = 0; i < GF_SecretInvItems[ADDINV_KEY2]; ++i ) {
Inv_AddItem(ID_KEY_ITEM2);
AddDisplayPickup(ID_KEY_ITEM2);
}
for( i = 0; i < GF_SecretInvItems[ADDINV_KEY3]; ++i ) {
Inv_AddItem(ID_KEY_ITEM3);
AddDisplayPickup(ID_KEY_ITEM3);
}
for( i = 0; i < GF_SecretInvItems[ADDINV_KEY4]; ++i ) {
Inv_AddItem(ID_KEY_ITEM4);
AddDisplayPickup(ID_KEY_ITEM4);
}
// Clear the array (secret bonus)
memset(GF_SecretInvItems, 0, sizeof(GF_SecretInvItems));
} else {
// Flares (level start)
for( i = 0; i < GF_Add2InvItems[ADDINV_FLARE]; ++i ) {
Inv_AddItem(ID_FLARE_ITEM);
}
// Medipacks (level start)
for( i = 0; i < GF_Add2InvItems[ADDINV_SMALL_MEDIPACK]; ++i ) {
Inv_AddItem(ID_SMALL_MEDIPACK_ITEM);
}
for( i = 0; i < GF_Add2InvItems[ADDINV_LARGE_MEDIPACK]; ++i ) {
Inv_AddItem(ID_LARGE_MEDIPACK_ITEM);
}
// Pickups (level start)
for( i = 0; i < GF_Add2InvItems[ADDINV_PICKUP1]; ++i ) {
Inv_AddItem(ID_PICKUP_ITEM1);
}
for( i = 0; i < GF_Add2InvItems[ADDINV_PICKUP2]; ++i ) {
Inv_AddItem(ID_PICKUP_ITEM2);
}
// Puzzles (level start)
for( i = 0; i < GF_Add2InvItems[ADDINV_PUZZLE1]; ++i ) {
Inv_AddItem(ID_PUZZLE_ITEM1);
}
for( i = 0; i < GF_Add2InvItems[ADDINV_PUZZLE2]; ++i ) {
Inv_AddItem(ID_PUZZLE_ITEM2);
}
for( i = 0; i < GF_Add2InvItems[ADDINV_PUZZLE3]; ++i ) {
Inv_AddItem(ID_PUZZLE_ITEM3);
}
for( i = 0; i < GF_Add2InvItems[ADDINV_PUZZLE4]; ++i ) {
Inv_AddItem(ID_PUZZLE_ITEM4);
}
// Keys (level start)
for( i = 0; i < GF_Add2InvItems[ADDINV_KEY1]; ++i ) {
Inv_AddItem(ID_KEY_ITEM1);
}
for( i = 0; i < GF_Add2InvItems[ADDINV_KEY2]; ++i ) {
Inv_AddItem(ID_KEY_ITEM2);
}
for( i = 0; i < GF_Add2InvItems[ADDINV_KEY3]; ++i ) {
Inv_AddItem(ID_KEY_ITEM3);
}
for( i = 0; i < GF_Add2InvItems[ADDINV_KEY4]; ++i ) {
Inv_AddItem(ID_KEY_ITEM4);
}
// Clear the array (level start)
memset(GF_Add2InvItems, 0, sizeof(GF_Add2InvItems));
}
}
// NOTE: not presented in the original game
int __cdecl GF_CurrentEvent() {
return CurrentEvent;
}
/*
* Inject function
*/
void Inject_Gameflow() {
INJECT(0x0041FA40, GF_LoadScriptFile);
INJECT(0x0041FC30, GF_DoFrontEndSequence);
INJECT(0x0041FC50, GF_DoLevelSequence);
INJECT(0x0041FCC0, GF_InterpretSequence);
INJECT(0x004201A0, GF_ModifyInventory);
}
================================================
FILE: game/gameflow.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef GAMEFLOW_H_INCLUDED
#define GAMEFLOW_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
BOOL __cdecl GF_LoadScriptFile(LPCTSTR fileName); // 0x0041FA40
BOOL __cdecl GF_DoFrontEndSequence(); // 0x0041FC30
int __cdecl GF_DoLevelSequence(DWORD levelID, GF_LEVEL_TYPE levelType); // 0x0041FC50
int __cdecl GF_InterpretSequence(__int16 *seq, GF_LEVEL_TYPE levelType, int seqType); // 0x0041FCC0
void __cdecl GF_ModifyInventory(int levelID, BOOL isSecret); // 0x004201A0
int __cdecl GF_CurrentEvent(); // NOTE: not presented in the original game
#endif // GAMEFLOW_H_INCLUDED
================================================
FILE: game/hair.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/hair.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Hair() {
// INJECT(0x00420E80, InitialiseHair);
// INJECT(0x00420F00, HairControl);
// INJECT(0x00421900, DrawHair);
}
================================================
FILE: game/hair.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef HAIR_H_INCLUDED
#define HAIR_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define InitialiseHair ((void(__cdecl*)(void)) 0x00420E80)
#define HairControl ((void(__cdecl*)(int)) 0x00420F00)
#define DrawHair ((void(__cdecl*)(void)) 0x00421900)
#endif // HAIR_H_INCLUDED
================================================
FILE: game/health.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/health.h"
#include "3dsystem/scalespr.h"
#include "game/text.h"
#include "specific/output.h"
#include "specific/sndpc.h"
#include "global/vars.h"
#define AMMO_XPOS_PC (-10)
#define AMMO_YPOS_PC (35)
#define AMMO_XPOS_PS (-16)
#define AMMO_YPOS_PS (64)
#ifdef FEATURE_HUD_IMPROVED
extern bool PsxBarPosEnabled;
#endif // FEATURE_HUD_IMPROVED
BOOL __cdecl FlashIt() {
static int counter = 0;
static BOOL state = FALSE;
if( counter > 0 ) {
--counter;
} else {
counter = 5;
state = !state;
}
return state;
}
void __cdecl DrawAssaultTimer() {
int xPos, yPos, d0, d1, d2;
int minutes, seconds, deciseconds;
DWORD scaleH, scaleV;
char timeString[16]; // NOTE: the original buffer was 8
// Exit if current level is not Assault or the timer is hidden
if( CurrentLevel != 0 || !IsAssaultTimerDisplay )
return;
deciseconds = SaveGame.statistics.timer % 30 / 3;
seconds = SaveGame.statistics.timer / 30 % 60;
minutes = SaveGame.statistics.timer / 30 / 60;
sprintf(timeString, "%d:%02d.%d", minutes, seconds, deciseconds);
#ifdef FEATURE_HUD_IMPROVED
scaleH = GetRenderScale(PHD_ONE);
scaleV = GetRenderScale(PHD_ONE);
xPos = PhdWinMaxX / 2 - GetRenderScale(50);
yPos = GetRenderScale(36);
d0 = GetRenderScale(20);
d1 = GetRenderScale(-6);
d2 = GetRenderScale(14);
#else // !FEATURE_HUD_IMPROVED
scaleH = PHD_ONE;
scaleV = PHD_ONE;
xPos = PhdWinMaxX / 2 - 50;
yPos = 36;
d0 = 20;
d1 = -6;
d2 = 14;
#endif // FEATURE_HUD_IMPROVED
for( char *str = timeString; *str != 0; ++str ) {
switch( *str ) {
case ':' : // colon
xPos += d1;
S_DrawScreenSprite2d(xPos, yPos, 0, scaleH, scaleV, (Objects[ID_ASSAULT_DIGITS].meshIndex + 10), 0x1000, 0);
xPos += d2;
break;
case '.' : // period
xPos += d1;
S_DrawScreenSprite2d(xPos, yPos, 0, scaleH, scaleV, (Objects[ID_ASSAULT_DIGITS].meshIndex + 11), 0x1000, 0);
xPos += d2;
break;
default : // any digit
S_DrawScreenSprite2d(xPos, yPos, 0, scaleH, scaleV, (Objects[ID_ASSAULT_DIGITS].meshIndex + (*str - '0')), 0x1000, 0);
xPos += d0;
break;
}
}
}
void __cdecl DrawGameInfo(BOOL pickupState) {
BOOL flashState;
DrawAmmoInfo();
DrawModeInfo();
if( OverlayStatus > 0 ) {
flashState = FlashIt();
DrawHealthBar(flashState);
DrawAirBar(flashState);
DrawPickups(pickupState);
DrawAssaultTimer();
}
T_DrawText();
}
void __cdecl DrawHealthBar(BOOL flashState) {
static int oldHitPoints;
int hitPoints;
// NOTE: fixes original game bug when health bar is visible while final cut scene
if( Lara.extra_anim && LaraItem->currentAnimState == EXTRA_FINALANIM ) {
return;
}
hitPoints = LaraItem->hitPoints;
CLAMP(hitPoints, 0, 1000);
if( oldHitPoints != hitPoints ){
oldHitPoints = hitPoints;
HealthBarTimer = 40; // 1.33 seconds
}
CLAMPL(HealthBarTimer, 0);
if( hitPoints <= 250 ) {
if( flashState == 0 ) {
S_DrawHealthBar(0);
} else {
#ifdef FEATURE_HUD_IMPROVED
S_DrawHealthBar(PHD_ONE * hitPoints / 1000);
#else // !FEATURE_HUD_IMPROVED
S_DrawHealthBar(hitPoints / 10);
#endif // FEATURE_HUD_IMPROVED
}
}
else if( HealthBarTimer > 0 || hitPoints <= 0 || Lara.gun_status == LGS_Ready ) {
#ifdef FEATURE_HUD_IMPROVED
S_DrawHealthBar(PHD_ONE * hitPoints / 1000);
#else // !FEATURE_HUD_IMPROVED
S_DrawHealthBar(hitPoints / 10);
#endif // FEATURE_HUD_IMPROVED
}
}
void __cdecl DrawAirBar(BOOL flashState) {
int air;
if( Lara.water_status != LWS_Underwater && Lara.water_status != LWS_Surface )
return;
air = Lara.air;
CLAMP(air, 0, 1800);
if( air <= 450 && flashState == 0 ) {
S_DrawAirBar(0);
} else {
#ifdef FEATURE_HUD_IMPROVED
S_DrawAirBar(PHD_ONE * air / 1800);
#else // !FEATURE_HUD_IMPROVED
S_DrawAirBar(100 * air / 1800);
#endif // FEATURE_HUD_IMPROVED
}
}
void __cdecl MakeAmmoString(char *str) {
for( ; *str != 0; ++str ) {
if( *str == 0x20 ) {
continue; // space chars
}
if( (BYTE)*str < 'A' ) {
*str += 1 - '0'; // ammo digit chars
} else {
*str += 0xC - 'A'; // ammo special chars
}
}
}
void __cdecl DrawAmmoInfo() {
char ammoString[80] = "";
if( Lara.gun_status != LGS_Ready || OverlayStatus <= 0 || SaveGame.bonusFlag ) {
if( AmmoTextInfo != NULL ) {
T_RemovePrint(AmmoTextInfo);
AmmoTextInfo = NULL;
}
return;
}
switch( Lara.gun_type ) {
case LGT_Magnums :
sprintf(ammoString, "%5d", (int)Lara.magnum_ammo);
break;
case LGT_Uzis :
sprintf(ammoString, "%5d", (int)Lara.uzi_ammo);
break;
case LGT_Shotgun :
sprintf(ammoString, "%5d", (int)(Lara.shotgun_ammo / 6));
break;
case LGT_Harpoon :
sprintf(ammoString, "%5d", (int)Lara.harpoon_ammo);
break;
case LGT_M16 :
sprintf(ammoString, "%5d", (int)Lara.m16_ammo);
break;
case LGT_Grenade :
sprintf(ammoString, "%5d", (int)Lara.grenade_ammo);
break;
default :
return;
}
MakeAmmoString(ammoString);
if( AmmoTextInfo == NULL ) {
#ifdef FEATURE_HUD_IMPROVED
if( PsxBarPosEnabled ) {
AmmoTextInfo = T_Print(AMMO_XPOS_PS, AMMO_YPOS_PS, 0, ammoString);
} else {
AmmoTextInfo = T_Print(AMMO_XPOS_PC, AMMO_YPOS_PC, 0, ammoString);
}
#else // !FEATURE_HUD_IMPROVED
AmmoTextInfo = T_Print(AMMO_XPOS_PC, AMMO_YPOS_PC, 0, ammoString);
#endif // FEATURE_HUD_IMPROVED
T_RightAlign(AmmoTextInfo, 1);
} else {
T_ChangeText(AmmoTextInfo, ammoString);
}
}
void __cdecl DrawPickups(BOOL pickupState) {
static int oldGameTimer = 0;
int time;
int x, y;
int cellH, cellV;
int column = 0;
time = SaveGame.statistics.timer - oldGameTimer;
oldGameTimer = SaveGame.statistics.timer;
if( time <= 0 || time >= 60 ) // 0..2 seconds
return;
#ifdef FEATURE_HUD_IMPROVED
cellH = MIN(PhdWinWidth, PhdWinHeight*320/200) / 10;
#else // !FEATURE_HUD_IMPROVED
cellH = PhdWinWidth / 10;
#endif // FEATURE_HUD_IMPROVED
cellV = cellH * 2 / 3;
x = PhdWinWidth - cellH;
y = PhdWinHeight - cellH;
for( int i = 0; i < 12; ++i ) {
if( pickupState ) {
PickupInfos[i].timer -= time;
CLAMPL(PickupInfos[i].timer, 0);
}
if( PickupInfos[i].timer > 0 ) {
if( ++column > 4 ) {
column = 1;
x = PhdWinWidth - cellH;
y -= cellV;
}
S_DrawPickup(x, y, 0x3000, PickupInfos[i].sprite, 0x1000);
x -= cellH;
}
}
}
void __cdecl AddDisplayPickup(__int16 itemID) {
if( itemID == ID_SECRET1 || itemID == ID_SECRET2 || itemID == ID_SECRET3 ) {
S_CDPlay(GF_GameFlow.secretTrack, FALSE);
}
for( int i = 0; i < 12; ++i ) {
if( PickupInfos[i].timer <= 0 ) {
PickupInfos[i].timer = 75; // 2.5 seconds
PickupInfos[i].sprite = Objects[itemID].meshIndex;
break;
}
}
}
void __cdecl DisplayModeInfo(char *modeString) {
if( modeString == NULL ) {
T_RemovePrint(DisplayModeTextInfo);
DisplayModeTextInfo = NULL;
return;
}
if( DisplayModeTextInfo != NULL ) {
T_ChangeText(DisplayModeTextInfo, modeString);
} else {
#ifdef FEATURE_HUD_IMPROVED
DisplayModeTextInfo = T_Print(-8, -8, 0, modeString);
#else // FEATURE_HUD_IMPROVED
DisplayModeTextInfo = T_Print(-16, -16, 0, modeString);
#endif // FEATURE_HUD_IMPROVED
T_RightAlign(DisplayModeTextInfo, 1);
T_BottomAlign(DisplayModeTextInfo, 1);
}
DisplayModeInfoTimer = 75; // 2.5 seconds
#ifdef FEATURE_HUD_IMPROVED
T_HideText(DisplayModeTextInfo, (OverlayStatus <= 0));
#endif // FEATURE_HUD_IMPROVED
}
void __cdecl DrawModeInfo() {
if( DisplayModeTextInfo != NULL && --DisplayModeInfoTimer == 0 ) {
T_RemovePrint(DisplayModeTextInfo);
DisplayModeTextInfo = NULL;
}
#ifdef FEATURE_HUD_IMPROVED
T_HideText(DisplayModeTextInfo, (OverlayStatus <= 0));
#endif // FEATURE_HUD_IMPROVED
}
void __cdecl InitialisePickUpDisplay() {
for( int i = 0; i < 12; ++i ) {
PickupInfos[i].timer = 0;
}
}
/*
* Inject function
*/
void Inject_Health() {
INJECT(0x00421980, FlashIt);
INJECT(0x004219B0, DrawAssaultTimer);
INJECT(0x00421B00, DrawGameInfo);
INJECT(0x00421B50, DrawHealthBar);
INJECT(0x00421C00, DrawAirBar);
INJECT(0x00421CA0, MakeAmmoString);
INJECT(0x00421CD0, DrawAmmoInfo);
INJECT(0x00421E20, InitialisePickUpDisplay);
INJECT(0x00421E40, DrawPickups);
INJECT(0x00421F40, AddDisplayPickup);
INJECT(0x00421FB0, DisplayModeInfo);
INJECT(0x00422030, DrawModeInfo);
}
================================================
FILE: game/health.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef HEALTH_H_INCLUDED
#define HEALTH_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
BOOL __cdecl FlashIt(); // 0x00421980
void __cdecl DrawAssaultTimer(); // 0x004219B0
void __cdecl DrawGameInfo(BOOL pickupState); // 0x00421B00
void __cdecl DrawHealthBar(BOOL flashState); // 0x00421B50
void __cdecl DrawAirBar(BOOL flashState); // 0x00421C00
void __cdecl MakeAmmoString(char *str); // 0x00421CA0
void __cdecl DrawAmmoInfo(); // 0x00421CD0
void __cdecl InitialisePickUpDisplay(); // 0x00421E20
void __cdecl DrawPickups(BOOL pickupState); // 0x00421E40
void __cdecl AddDisplayPickup(__int16 itemID); // 0x00421F40
void __cdecl DisplayModeInfo(char *modeString); // 0x00421FB0
void __cdecl DrawModeInfo(); // 0x00422030
#endif // HEALTH_H_INCLUDED
================================================
FILE: game/inventory.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Copyright (c) 2019 TokyoSU
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/inventory.h"
#include "3dsystem/3d_gen.h"
#include "3dsystem/scalespr.h"
#include "game/demo.h"
#include "game/draw.h"
#include "game/health.h"
#include "game/invfunc.h"
#include "game/laramisc.h"
#include "game/sound.h"
#include "game/text.h"
#include "specific/display.h"
#include "specific/frontend.h"
#include "specific/input.h"
#include "specific/option.h"
#include "specific/output.h"
#include "specific/sndpc.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/joy_output.h"
#endif // FEATURE_INPUT_IMPROVED
#ifdef FEATURE_HUD_IMPROVED
extern void RemoveJoystickHintText(bool isSelect, bool isContinue, bool isDeselect);
extern void DisplayJoystickHintText(bool isSelect, bool isContinue, bool isDeselect);
extern void DisplayVolumeBars(bool isSmooth);
extern bool PsxFovEnabled;
extern DWORD InvTextBoxMode;
#endif // FEATURE_HUD_IMPROVED
typedef enum {
RINGSTATE_OPENING,
RINGSTATE_OPEN,
RINGSTATE_CLOSING,
RINGSTATE_MAIN2OPTION,
RINGSTATE_MAIN2KEYS,
RINGSTATE_KEYS2MAIN,
RINGSTATE_OPTION2MAIN,
RINGSTATE_SELECTING,
RINGSTATE_SELECTED,
RINGSTATE_DESELECTING,
RINGSTATE_DESELECT,
RINGSTATE_CLOSING_ITEM,
RINGSTATE_EXITING_INVENTORY,
RINGSTATE_DONE
} RING_STATES;
#define PASS_SPINE (0x01)
#define PASS_FRONT (0x02)
#define PASS_INFRONT (0x04)
#define PASS_PAGE2 (0x08)
#define PASS_BACK (0x10)
#define PASS_INBACK (0x20)
#define PASS_PAGE1 (0x40)
#define PASS_BASE (PASS_FRONT|PASS_SPINE|PASS_BACK)
int __cdecl Display_Inventory(INVENTORY_MODE invMode) {
BOOL isDemoNeeded = FALSE;
BOOL isPassOpen = FALSE;
int itemAnimateFrame = 0;
__int16 itemRotation = 0;
INVENTORY_ITEM *item = NULL;
RING_INFO ring;
PHD_3DPOS viewPos;
INV_MOTION_INFO motion;
memset(&ring, 0, sizeof(RING_INFO));
memset(&motion, 0, sizeof(INV_MOTION_INFO));
if( invMode == INV_KeysMode && !InvKeyObjectsCount ) {
InventoryChosen = -1;
return 0;
}
T_RemovePrint(AmmoTextInfo);
AmmoTextInfo = NULL;
AlterFOV(80*PHD_DEGREE);
InventoryMode = invMode;
int nTicks = TICKS_PER_FRAME;
Construct_Inventory();
if( InventoryMode == INV_TitleMode ) {
S_FadeInInventory(0);
} else {
S_FadeInInventory(1);
}
SOUND_Stop();
if( InventoryMode != INV_TitleMode ) {
#ifdef FEATURE_AUDIO_IMPROVED
extern double InventoryMusicMute;
double volume = (1.0 - InventoryMusicMute) * (double)(MusicVolume * 25 + 5);
if( volume >= 1.0 ) {
S_CDVolume((DWORD)volume);
} else {
S_CDVolume(0);
}
#else // FEATURE_AUDIO_IMPROVED
S_CDVolume(0); // NOTE: Core supposed to pause CD Audio this way
#endif // FEATURE_AUDIO_IMPROVED
}
switch( InventoryMode ) {
case INV_TitleMode :
case INV_SaveMode :
case INV_LoadMode :
case INV_DeathMode :
Inv_RingInit(&ring, RING_Option, InvOptionList, InvOptionObjectsCount, InvOptionCurrent, &motion);
break;
case INV_KeysMode :
Inv_RingInit(&ring, RING_Keys, InvKeysList, InvKeyObjectsCount, InvMainCurrent, &motion);
break;
default :
if (InvMainObjectsCount) {
Inv_RingInit(&ring, RING_Main, InvMainList, InvMainObjectsCount, InvMainCurrent, &motion);
} else {
Inv_RingInit(&ring, RING_Option, InvOptionList, InvOptionObjectsCount, InvOptionCurrent, &motion);
}
break;
}
PlaySoundEffect(111, NULL, SFX_ALWAYS);
nTicks = TICKS_PER_FRAME;
do {
if( InventoryMode == INV_TitleMode && CD_TrackID > 0 ) {
S_CDLoop();
}
Inv_RingCalcAdders(&ring, 24);
S_UpdateInput();
if( InvDemoMode ) {
if( InputStatus ) {
return GF_GameFlow.onDemo_Interrupt;
}
GetDemoInput();
if( InputStatus == (DWORD)-1 ) {
return GF_GameFlow.onDemo_End;
}
} else if( InputStatus ) {
NoInputCounter = 0;
}
InputDB = GetDebouncedInput(InputStatus);
if( InventoryMode != INV_TitleMode || InputStatus || InputDB ) {
NoInputCounter = 0;
IsResetFlag = FALSE;
} else if( GF_GameFlow.num_Demos || CHK_ANY(GF_GameFlow.flags, GFF_NoInputTimeout) ) {
if( ++NoInputCounter > GF_GameFlow.noInput_Time ) {
isDemoNeeded = TRUE;
IsResetFlag = TRUE;
}
}
if( StopInventory ) {
return GF_EXIT_TO_TITLE;
}
if( (InventoryMode == INV_SaveMode ||
InventoryMode == INV_LoadMode ||
InventoryMode == INV_DeathMode) && !isPassOpen )
{
InputStatus = 0;
InputDB = IN_SELECT;
}
for( int i = 0; i < nTicks; ++i ) {
if( IsInvOptionsDelay ) {
if( InvOptionsDelayCounter ) {
--InvOptionsDelayCounter;
} else {
IsInvOptionsDelay = FALSE;
}
}
Inv_RingDoMotions(&ring);
}
ring.camera.z = (ring.radius + 0x256);
S_InitialisePolyList(0);
if( InventoryMode == INV_TitleMode ) {
DoInventoryPicture();
} else {
DoInventoryBackground();
}
S_AnimateTextures(nTicks);
Inv_RingGetView(&ring, &viewPos);
phd_GenerateW2V(&viewPos);
Inv_RingLight(&ring);
phd_PushMatrix();
phd_TranslateAbs(ring.ringPos.x, ring.ringPos.y, ring.ringPos.z);
phd_RotYXZ(ring.ringPos.rotY, ring.ringPos.rotX, ring.ringPos.rotZ);
itemRotation = 0;
for( int i = 0; i < ring.objCount; ++i ) {
item = ring.itemList[i];
if( i == ring.currentObj ) {
for( int j = 0; j < nTicks; ++j ) {
if( ring.isRotating ) {
LsAdder = 0x1400;
if( item->zRot > 0 ) {
item->zRot -= 0x200;
} else if( item->zRot < 0 ) {
item->zRot += 0x200;
}
} else if( motion.status == RINGSTATE_SELECTED
|| motion.status == RINGSTATE_DESELECTING
|| motion.status == RINGSTATE_SELECTING
|| motion.status == RINGSTATE_DESELECT
|| motion.status == RINGSTATE_CLOSING_ITEM )
{
LsAdder = 0x1000;
if( item->zRot != item->yRot ) {
if( item->yRot <= item->zRot || item->yRot >= item->zRot + PHD_180 ) {
item->zRot -= 0x400;
} else {
item->zRot += 0x400;
}
item->zRot &= 0xFC00;
}
} else if( ring.objCount == 1 || !CHK_ANY(InputStatus, IN_LEFT|IN_RIGHT) ) {
LsAdder = 0x1000;
item->zRot += 0x100;
}
}
if( (motion.status == RINGSTATE_OPEN
|| motion.status == RINGSTATE_SELECTING
|| motion.status == RINGSTATE_SELECTED
|| motion.status == RINGSTATE_DESELECTING
|| motion.status == RINGSTATE_DESELECT
|| motion.status == RINGSTATE_CLOSING_ITEM)
&& !ring.isRotating
&& !CHK_ANY(InputStatus, IN_LEFT|IN_RIGHT) )
{
RingNotActive(item);
}
} else {
LsAdder = 0x1400;
for( int i = 0; i < nTicks; ++i ) {
if( item->zRot > 0 ) {
item->zRot -= 0x100;
} else if( item->zRot < 0 ) {
item->zRot += 0x100;
}
}
}
if( motion.status == RINGSTATE_OPEN
|| motion.status == RINGSTATE_SELECTING
|| motion.status == RINGSTATE_SELECTED
|| motion.status == RINGSTATE_DESELECTING
|| motion.status == RINGSTATE_DESELECT
|| motion.status == RINGSTATE_CLOSING_ITEM )
{
RingIsOpen(&ring);
} else {
RingIsNotOpen();
}
if( motion.status == RINGSTATE_OPENING
|| motion.status == RINGSTATE_CLOSING
|| motion.status == RINGSTATE_MAIN2OPTION
|| motion.status == RINGSTATE_OPTION2MAIN
|| motion.status == RINGSTATE_EXITING_INVENTORY
|| motion.status == RINGSTATE_DONE
|| ring.isRotating )
{
RingActive();
}
phd_PushMatrix();
phd_RotYXZ(itemRotation, 0, 0);
phd_TranslateRel(ring.radius, 0, 0);
phd_RotYXZ(PHD_90, item->xRotPt, 0);
DrawInventoryItem(item);
phd_PopMatrix();
itemRotation += ring.angleAdder;
}
phd_PopMatrix();
DrawModeInfo();
T_DrawText();
#ifdef FEATURE_HUD_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode &&
ring.itemList[ring.currentObj]->objectID == ID_SOUND_OPTION )
{
static bool isSoundSelected = false;
if( motion.status == RINGSTATE_SELECTING && isSoundSelected ) {
isSoundSelected = false;
}
if( motion.status == RINGSTATE_SELECTED ) {
DisplayVolumeBars(isSoundSelected);
if( !isSoundSelected ) isSoundSelected = true;
}
}
#endif // FEATURE_HUD_IMPROVED
S_OutputPolyList();
SOUND_EndScene();
Camera.numberFrames = nTicks = S_DumpScreen();
if( CurrentLevel != 0 ) { // not Lara home
SaveGame.statistics.timer += nTicks / TICKS_PER_FRAME;
}
if( !ring.isRotating ) {
switch( motion.status ) {
case RINGSTATE_OPEN :
#ifdef FEATURE_HUD_IMPROVED
DisplayJoystickHintText(true, false, InventoryMode != INV_TitleMode && InventoryMode != INV_DeathMode);
#endif // FEATURE_HUD_IMPROVED
if( CHK_ANY(InputStatus, IN_RIGHT) && ring.objCount > 1 ) {
Inv_RingRotateLeft(&ring);
PlaySoundEffect(108, 0, SFX_ALWAYS);
break;
}
if( CHK_ANY(InputStatus, IN_LEFT) && ring.objCount > 1 ) {
Inv_RingRotateRight(&ring);
PlaySoundEffect(108, 0, SFX_ALWAYS);
break;
}
if( IsResetFlag || (InventoryMode != INV_TitleMode && CHK_ANY(InputDB, IN_DESELECT|IN_OPTION)) ) {
PlaySoundEffect(112, 0, SFX_ALWAYS);
InventoryChosen = -1;
if( ring.type != RING_Main ) {
InvOptionCurrent = ring.currentObj;
} else {
InvMainCurrent = ring.currentObj;
}
if (InventoryMode == INV_TitleMode) {
S_FadeOutInventory(FALSE);
} else {
S_FadeOutInventory(TRUE);
}
Inv_RingMotionSetup(&ring, RINGSTATE_CLOSING, RINGSTATE_DONE, 32);
Inv_RingMotionRadius(&ring, 0);
Inv_RingMotionCameraPos(&ring, -0x600);
Inv_RingMotionRotation(&ring, -PHD_180, ring.ringPos.rotY + PHD_180);
InputStatus = 0;
InputDB = 0;
}
if( CHK_ANY(InputDB, IN_SELECT) ) {
if( (InventoryMode == INV_SaveMode || InventoryMode == INV_LoadMode || InventoryMode == INV_DeathMode) && !isPassOpen ) {
isPassOpen = TRUE;
}
SoundOptionLine = 0;
switch (ring.type) {
case RING_Main :
InvMainCurrent = ring.currentObj;
item = InvMainList[ring.currentObj];
break;
case RING_Option :
InvOptionCurrent = ring.currentObj;
item = InvOptionList[ring.currentObj];
break;
case RING_Keys :
default :
InvKeysCurrent = ring.currentObj;
item = InvKeysList[ring.currentObj];
break;
}
item->goalFrame = item->openFrame;
item->animDirection = 1;
Inv_RingMotionSetup(&ring, RINGSTATE_SELECTING, RINGSTATE_SELECTED, 16);
Inv_RingMotionRotation(&ring, 0, -PHD_90 - ring.angleAdder * ring.currentObj);
Inv_RingMotionItemSelect(&ring, item);
InputStatus = 0;
InputDB = 0;
switch (item->objectID) {
case ID_COMPASS_OPTION :
PlaySoundEffect(113, 0, SFX_ALWAYS);
break;
case ID_PHOTO_OPTION :
PlaySoundEffect(109, 0, SFX_ALWAYS);
break;
case ID_PISTOL_OPTION :
case ID_SHOTGUN_OPTION :
case ID_MAGNUM_OPTION :
case ID_UZI_OPTION :
case ID_HARPOON_OPTION :
case ID_M16_OPTION :
case ID_GRENADE_OPTION :
PlaySoundEffect(114, 0, SFX_ALWAYS);
break;
default :
PlaySoundEffect(111, 0, SFX_ALWAYS);
break;
}
}
if( CHK_ANY(InputDB, IN_FORWARD) && InventoryMode != INV_TitleMode && InventoryMode != INV_KeysMode ) {
if( ring.type == RING_Main ) {
if( InvKeyObjectsCount ) {
Inv_RingMotionSetup(&ring, RINGSTATE_CLOSING, RINGSTATE_MAIN2KEYS, 24);
Inv_RingMotionRadius(&ring, 0);
Inv_RingMotionRotation(&ring, -PHD_180, ring.ringPos.rotY - PHD_180);
Inv_RingMotionCameraPitch(&ring, 0x2000);
motion.misc = 0x2000;
}
InputStatus = 0;
InputDB = 0;
} else if( ring.type == RING_Option ) {
if( InvMainObjectsCount ) {
Inv_RingMotionSetup(&ring, RINGSTATE_CLOSING, RINGSTATE_OPTION2MAIN, 24);
Inv_RingMotionRadius(&ring, 0);
Inv_RingMotionRotation(&ring, -PHD_180, ring.ringPos.rotY - PHD_180);
Inv_RingMotionCameraPitch(&ring, 0x2000);
motion.misc = 0x2000;
}
InputDB = 0;
}
} else if( InputDB & IN_BACK && InventoryMode != INV_TitleMode && InventoryMode != INV_KeysMode ) {
if( ring.type == RING_Keys ) {
if (InvMainObjectsCount) {
Inv_RingMotionSetup(&ring, RINGSTATE_CLOSING, RINGSTATE_KEYS2MAIN, 24);
Inv_RingMotionRadius(&ring, 0);
Inv_RingMotionRotation(&ring, -PHD_180, ring.ringPos.rotY - PHD_180);
Inv_RingMotionCameraPitch(&ring, -0x2000);
motion.misc = -0x2000;
}
InputStatus = 0;
InputDB = 0;
} else if( ring.type == RING_Main ) {
if( InvOptionObjectsCount || !(GF_GameFlow.flags & GFF_LockoutOptionRing) ) {
Inv_RingMotionSetup(&ring, RINGSTATE_CLOSING, RINGSTATE_MAIN2OPTION, 24);
Inv_RingMotionRadius(&ring, 0);
Inv_RingMotionRotation(&ring, -PHD_180, ring.ringPos.rotY - PHD_180);
Inv_RingMotionCameraPitch(&ring, -0x2000);
motion.misc = -0x2000;
}
InputDB = 0;
}
}
break;
case RINGSTATE_MAIN2OPTION:
Inv_RingMotionSetup(&ring, RINGSTATE_OPENING, RINGSTATE_OPEN, 24);
Inv_RingMotionRadius(&ring, 0x2B0);
ring.cameraPitch = -motion.misc;
motion.cameraTarget_pitch = 0;
motion.cameraRate_pitch = motion.misc / 24;
ring.itemList = InvOptionList;
ring.type = RING_Option;
InvMainCurrent = ring.currentObj;
ring.objCount = InvOptionObjectsCount;
ring.currentObj = InvOptionCurrent;
Inv_RingCalcAdders(&ring, 24);
Inv_RingMotionRotation(&ring, -PHD_180, -PHD_90 - ring.angleAdder * ring.currentObj);
ring.ringPos.rotY = motion.rotateTarget - PHD_180;
break;
case RINGSTATE_MAIN2KEYS :
Inv_RingMotionSetup(&ring, RINGSTATE_OPENING, RINGSTATE_OPEN, 24);
Inv_RingMotionRadius(&ring, 0x2B0);
ring.cameraPitch = -motion.misc;
motion.cameraRate_pitch = motion.misc / 24;
motion.cameraTarget_pitch = 0;
InvMainCurrent = ring.currentObj;
InvMainObjectsCount = ring.objCount;
ring.itemList = InvKeysList;
ring.type = RING_Keys;
ring.objCount = InvKeyObjectsCount;
ring.currentObj = InvKeysCurrent;
Inv_RingCalcAdders(&ring, 24);
Inv_RingMotionRotation(&ring, -PHD_180, -PHD_90 - ring.angleAdder * ring.currentObj);
ring.ringPos.rotY = motion.rotateTarget - PHD_180;
break;
case RINGSTATE_KEYS2MAIN :
Inv_RingMotionSetup(&ring, RINGSTATE_OPENING, RINGSTATE_OPEN, 24);
Inv_RingMotionRadius(&ring, 0x2B0);
ring.cameraPitch = -motion.misc;
motion.cameraRate_pitch = motion.misc / 24;
motion.cameraTarget_pitch = 0;
ring.itemList = InvMainList;
ring.type = RING_Main;
InvKeyObjectsCount = ring.objCount;
InvKeysCurrent = ring.currentObj;
ring.objCount = InvMainObjectsCount;
ring.currentObj = InvMainCurrent;
Inv_RingCalcAdders(&ring, 24);
Inv_RingMotionRotation(&ring, -PHD_180, -PHD_90 - ring.angleAdder * ring.currentObj);
ring.ringPos.rotY = motion.rotateTarget - PHD_180;
break;
case RINGSTATE_OPTION2MAIN :
Inv_RingMotionSetup(&ring, RINGSTATE_OPENING, RINGSTATE_OPEN, 24);
Inv_RingMotionRadius(&ring, 0x2B0);
ring.cameraPitch = -motion.misc;
motion.cameraRate_pitch = motion.misc / 24;
motion.cameraTarget_pitch = 0;
ring.itemList = InvMainList;
ring.type = RING_Main;
InvOptionObjectsCount = ring.objCount;
InvOptionCurrent = ring.currentObj;
ring.objCount = InvMainObjectsCount;
ring.currentObj = InvMainCurrent;
Inv_RingCalcAdders(&ring, 24);
Inv_RingMotionRotation(&ring, -PHD_180, -PHD_90 - ring.angleAdder * ring.currentObj);
ring.ringPos.rotY = motion.rotateTarget - PHD_180;
break;
case RINGSTATE_SELECTED :
#ifdef FEATURE_HUD_IMPROVED
DisplayJoystickHintText(true, false, InventoryMode != INV_DeathMode);
#endif // FEATURE_HUD_IMPROVED
item = ring.itemList[ring.currentObj];
if( item->objectID == ID_PASSPORT_CLOSED ) {
item->objectID = ID_PASSPORT_OPTION;
}
for( int i = 0; i < nTicks; ++i ) {
itemAnimateFrame = 0;
if( item->zRot == item->yRot ) {
itemAnimateFrame = AnimateInventoryItem(item);
}
}
if( !itemAnimateFrame && !IsInvOptionsDelay ) {
do_inventory_options(item);
if( CHK_ANY(InputDB, IN_DESELECT) ) {
item->sprites = NULL;
Inv_RingMotionSetup(&ring, RINGSTATE_CLOSING_ITEM, RINGSTATE_DESELECT, 0);
InputStatus = 0;
InputDB = 0;
if( InventoryMode == INV_SaveMode || InventoryMode == INV_LoadMode ) {
Inv_RingMotionSetup(&ring, RINGSTATE_CLOSING_ITEM, RINGSTATE_EXITING_INVENTORY, 0);
InputDB = 0;
InputStatus = 0;
}
}
if( CHK_ANY(InputDB, IN_SELECT) ) {
item->sprites = NULL;
InventoryChosen = item->objectID;
if( ring.type == RING_Main ) {
InvMainCurrent = ring.currentObj;
} else {
InvOptionCurrent = ring.currentObj;
}
if( InventoryMode == INV_TitleMode
&& (item->objectID == ID_DETAIL_OPTION
|| item->objectID == ID_SOUND_OPTION
|| item->objectID == ID_CONTROL_OPTION
|| item->objectID == ID_GAMMA_OPTION) )
{
Inv_RingMotionSetup(&ring, RINGSTATE_CLOSING_ITEM, RINGSTATE_DESELECT, 0);
} else {
Inv_RingMotionSetup(&ring, RINGSTATE_CLOSING_ITEM, RINGSTATE_EXITING_INVENTORY, 0);
}
InputStatus = 0;
InputDB = 0;
}
}
break;
case RINGSTATE_DESELECT :
#ifdef FEATURE_HUD_IMPROVED
RemoveJoystickHintText(false, false, InventoryMode == INV_TitleMode || InventoryMode == INV_DeathMode);
#endif // FEATURE_HUD_IMPROVED
PlaySoundEffect(112, 0, SFX_ALWAYS);
Inv_RingMotionSetup(&ring, RINGSTATE_DESELECTING, RINGSTATE_OPEN, 16);
Inv_RingMotionRotation(&ring, 0, -PHD_90 - ring.angleAdder * ring.currentObj);
InputStatus = 0;
InputDB = 0;
break;
case RINGSTATE_CLOSING_ITEM :
item = ring.itemList[ring.currentObj];
for( int i = 0; i < nTicks; ++i ) {
if( !AnimateInventoryItem(item) ) {
if( item->objectID == ID_PASSPORT_OPTION ) {
item->objectID = ID_PASSPORT_CLOSED;
item->currentFrame = 0;
}
motion.framesCount = 16;
motion.status = motion.statusTarget;
Inv_RingMotionItemDeselect(&ring, item);
}
}
break;
case RINGSTATE_CLOSING :
#ifdef FEATURE_HUD_IMPROVED
RemoveJoystickHintText(true, false, true);
#endif // FEATURE_HUD_IMPROVED
break;
case RINGSTATE_EXITING_INVENTORY :
#ifdef FEATURE_HUD_IMPROVED
RemoveJoystickHintText(true, false, true);
#endif // FEATURE_HUD_IMPROVED
if( !motion.framesCount ) {
if( InventoryMode == INV_TitleMode ) {
S_FadeOutInventory(FALSE);
} else {
S_FadeOutInventory(TRUE);
}
Inv_RingMotionSetup(&ring, RINGSTATE_CLOSING, RINGSTATE_DONE, 32);
Inv_RingMotionRadius(&ring, 0);
Inv_RingMotionCameraPos(&ring, -0x600);
Inv_RingMotionRotation(&ring, -PHD_180, ring.ringPos.rotY - PHD_180);
}
break;
}
}
#ifdef FEATURE_INPUT_IMPROVED
UpdateJoyOutput(false);
#endif // FEATURE_INPUT_IMPROVED
} while( motion.status != RINGSTATE_DONE );
#ifdef FEATURE_HUD_IMPROVED
RemoveJoystickHintText(true, false, true);
#endif // FEATURE_HUD_IMPROVED
RemoveInventoryText();
S_FinishInventory();
IsInventoryActive = 0;
if( IsResetFlag ) {
return GF_EXIT_TO_TITLE;
}
if( isDemoNeeded ) {
return GF_START_DEMO;
}
switch( InventoryChosen ) {
case ID_PASSPORT_OPTION :
if( MusicVolume && InventoryExtraData[0] == 1 ) {
S_CDVolume(25 * MusicVolume + 5);
}
return 1;
case ID_PHOTO_OPTION :
if( CHK_ANY(GF_GameFlow.flags, GFF_GymEnabled) ) {
InventoryExtraData[1] = 0;
return 1;
}
break;
case ID_PISTOL_OPTION :
UseItem(ID_PISTOL_OPTION);
break;
case ID_SHOTGUN_OPTION :
UseItem(ID_SHOTGUN_OPTION);
break;
case ID_MAGNUM_OPTION :
UseItem(ID_MAGNUM_OPTION);
break;
case ID_UZI_OPTION :
UseItem(ID_UZI_OPTION);
break;
case ID_HARPOON_OPTION :
UseItem(ID_HARPOON_OPTION);
break;
case ID_M16_OPTION :
UseItem(ID_M16_OPTION);
break;
case ID_GRENADE_OPTION :
UseItem(ID_GRENADE_OPTION);
break;
case ID_SMALL_MEDIPACK_OPTION :
UseItem(ID_SMALL_MEDIPACK_OPTION);
break;
case ID_LARGE_MEDIPACK_OPTION :
UseItem(ID_LARGE_MEDIPACK_OPTION);
break;
case ID_FLARES_OPTION :
UseItem(ID_FLARES_OPTION);
break;
}
if( MusicVolume && InventoryMode != INV_TitleMode ) {
#ifdef FEATURE_AUDIO_IMPROVED
if( Camera.underwater ) {
extern double UnderwaterMusicMute;
double volume = (1.0 - UnderwaterMusicMute) * (double)(MusicVolume * 25 + 5);
if( volume >= 1.0 ) {
S_CDVolume((DWORD)volume);
} else {
S_CDVolume(0);
}
} else {
S_CDVolume(MusicVolume * 25 + 5);
}
#else // FEATURE_AUDIO_IMPROVED
S_CDVolume(MusicVolume * 25 + 5);
#endif // FEATURE_AUDIO_IMPROVED
}
return 0;
}
void __cdecl Construct_Inventory() {
S_SetupAboveWater(FALSE);
if( InventoryMode != INV_TitleMode ) {
TempVideoAdjust(HiRes, 1.0);
}
memset(InventoryExtraData, 0, sizeof(InventoryExtraData));
PhdWinLeft = 0;
PhdWinTop = 0;
PhdWinRight = PhdWinMaxX;
PhdWinBottom = PhdWinMaxY;
IsInventoryActive = 1;
InventoryChosen = 0;
InvOptionObjectsCount = ARRAY_SIZE(InvOptionList) - ((InventoryMode == INV_TitleMode) ? 0 : 1);
for( int i = 0; i < InvMainObjectsCount; ++i ) {
InvMainList[i]->currentFrame = 0;
InvMainList[i]->meshesDrawn = InvMainList[i]->meshesSel;
InvMainList[i]->goalFrame = 0;
InvMainList[i]->animCount = 0;
InvMainList[i]->zRot = 0;
}
for( int i = 0; i < InvOptionObjectsCount; ++i ) {
InvOptionList[i]->currentFrame = 0;
InvOptionList[i]->goalFrame = 0;
InvOptionList[i]->animCount = 0;
InvOptionList[i]->zRot = 0;
}
InvMainCurrent = 0;
if( GymInvOpenEnabled && InventoryMode == INV_TitleMode && !CHK_ANY(GF_GameFlow.flags, GFF_LoadSaveDisabled) && CHK_ANY(GF_GameFlow.flags, GFF_GymEnabled) ) {
InvOptionCurrent = ARRAY_SIZE(InvOptionList) - 1;
} else {
InvOptionCurrent = 0;
GymInvOpenEnabled = FALSE;
}
SoundOptionLine = 0;
#ifdef FEATURE_HUD_IMPROVED
double scale = (double)GetRenderScale(480) / (double)GetRenderHeight();
if( scale < 1.5 ) {
InvCompassOption.yTransSel = PsxFovEnabled ? -140 : -170;
} else if( scale < 1.7) {
InvCompassOption.yTransSel = -15 - (scale - 1.5) * 35.0;
} else {
InvCompassOption.yTransSel = -22 - (scale - 1.7) / 0.0075;
}
#endif // FEATURE_HUD_IMPROVED
}
void __cdecl SelectMeshes(INVENTORY_ITEM *invItem) {
if( invItem->objectID == ID_PASSPORT_OPTION ) {
if (invItem->currentFrame < 4) {
invItem->meshesDrawn = PASS_BASE | PASS_INFRONT;
} else if (invItem->currentFrame <= 16) {
invItem->meshesDrawn = PASS_BASE | PASS_INFRONT | PASS_PAGE1;
} else if (invItem->currentFrame < 19) {
invItem->meshesDrawn = PASS_BASE | PASS_INFRONT | PASS_PAGE1 | PASS_PAGE2;
} else if (invItem->currentFrame == 19) {
invItem->meshesDrawn = PASS_BASE | PASS_PAGE1 | PASS_PAGE2;
} else if (invItem->currentFrame < 24) {
invItem->meshesDrawn = PASS_BASE | PASS_PAGE1 | PASS_PAGE2 | PASS_INBACK;
} else if (invItem->currentFrame < 29) {
invItem->meshesDrawn = PASS_BASE | PASS_PAGE2 | PASS_INBACK;
} else if (invItem->currentFrame == 29) {
invItem->meshesDrawn = PASS_BASE;
}
} else if (invItem->objectID != ID_GAMMA_OPTION) {
invItem->meshesDrawn = ~0;
}
}
int __cdecl AnimateInventoryItem(INVENTORY_ITEM *invItem) {
int frame = invItem->currentFrame;
int animID = invItem->animCount;
if( frame == invItem->goalFrame ) {
SelectMeshes(invItem);
return 0;
}
if( animID ) {
invItem->animCount = (animID - 1);
SelectMeshes(invItem);
return 1;
}
invItem->animCount = invItem->animSpeed;
invItem->currentFrame = (frame + invItem->animDirection);
if( invItem->currentFrame >= invItem->framesTotal ) {
invItem->currentFrame = 0;
} else if( invItem->currentFrame < 0 ) {
invItem->currentFrame = invItem->framesTotal - 1;
}
SelectMeshes(invItem);
return 1;
}
void __cdecl DrawInventoryItem(INVENTORY_ITEM* invItem) {
int hours=0, minutes=0, seconds=0, totsec=0, clip=0;
short* frame[2] = {0};
if( invItem->objectID == ID_COMPASS_OPTION ) {
totsec = SaveGame.statistics.timer/30;
seconds = totsec%60;
minutes = ((totsec%3600)*-91)/5;
hours = ((totsec/12)*-91)/5;
seconds *= -1092;
}
phd_TranslateRel(0, invItem->yTrans, invItem->zTrans);
phd_RotYXZ(invItem->zRot, invItem->yRotSel, 0);
OBJECT_INFO *obj = &Objects[invItem->objectID];
if( !obj->loaded ) {
return;
}
if( obj->nMeshes < 0 ) {
S_DrawSprite(0, 0, 0, 0, obj->meshIndex, 0, 0);
return;
}
if( invItem->sprites ) {
clip = PhdMatrixPtr->_23;
int sx = PhdWinCenterX + PhdMatrixPtr->_03 / (clip / PhdPersp);
int sy = PhdWinCenterY + PhdMatrixPtr->_13 / (clip / PhdPersp);
INVENTORY_SPRITE *sprite = (INVENTORY_SPRITE*)invItem->sprites;
for( int i = sprite->y; sprite->shape != 0; i += 4) {
if (clip < PhdNearZ || clip > PhdFarZ) {
break;
}
for( int j = sprite->shape; j != 0; j++ ) {
switch( j ) {
case 1 :
S_DrawScreenSprite(sx+sprite->x, sy+sprite->y,sprite->z,sprite->param1,sprite->param2,StaticObjects[ID_ALPHABET].meshIndex+sprite->invColour,0x1000,0);
break;
case 2 :
S_DrawScreenLine(sx+sprite->x, sy+sprite->y,sprite->z,sprite->param1,sprite->param2,sprite->invColour,(D3DCOLOR*)sprite->gour,0);
break;
case 3 :
S_DrawScreenBox(sx+sprite->x, sy+sprite->y,sprite->z,sprite->param1,sprite->param2,sprite->invColour,(GOURAUD_OUTLINE*)sprite->gour,0);
break;
case 4 :
S_DrawScreenFBox(sx+sprite->x, sy+sprite->y,sprite->z,sprite->param1,sprite->param2,sprite->invColour,(GOURAUD_FILL*)sprite->gour,0);
break;
}
}
}
}
phd_PushMatrix();
frame[0] = (&obj->frameBase[invItem->currentFrame * (Anims[obj->animIndex].interpolation >> 8)]);
clip = S_GetObjectBounds(frame[0]);
if( clip ) {
phd_TranslateRel((int)*(frame[0] + 6), (int)*(frame[0] + 7), (int)*(frame[0] + 8));
UINT16 *rotation = (UINT16 *)(frame[0] + 9);
phd_RotYXZsuperpack(&rotation, 0);
__int16 mesh = obj->meshIndex;
int *bones = &AnimBones[obj->boneIndex];
__int16 mesh_num = 1;
if( mesh_num & invItem->meshesDrawn ) {
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(invItem->objectID, 0);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons(MeshPtr[mesh], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
}
for( int i = obj->nMeshes-1; i > 0; --i ) {
++mesh;
mesh_num <<= 1;
int pushpop = *(bones++);
if( CHK_ANY(pushpop, 1) ) {
phd_PopMatrix();
}
if( CHK_ANY(pushpop, 2) ) {
phd_PushMatrix();
}
phd_TranslateRel(bones[0], bones[1], bones[2]);
phd_RotYXZsuperpack(&rotation, 0);
if( invItem->objectID == ID_COMPASS_OPTION ) {
switch( i ) {
case 2 :
phd_RotZ(seconds);
invItem->reserved2 = invItem->reserved1;
invItem->reserved1 = seconds;
break;
case 3 :
phd_RotZ(minutes);
break;
case 4 :
phd_RotZ(hours);
break;
}
}
if( CHK_ANY(mesh_num, invItem->meshesDrawn) ) {
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(invItem->objectID, obj->nMeshes-i);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons(MeshPtr[mesh], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
}
bones += 3;
}
}
phd_PopMatrix();
}
DWORD __cdecl GetDebouncedInput(DWORD input) {
static DWORD oldInput = 0;
DWORD result = input & ~oldInput;
#ifdef FEATURE_INPUT_IMPROVED
static int holdBack = -12;
static int holdForward = -12;
if( !CHK_ANY(input, IN_BACK) || CHK_ANY(input, IN_FORWARD) ) {
holdBack = -12;
} else if( CHK_ANY(input, IN_BACK) && ++holdBack >= 3 ) {
result |= IN_BACK;
holdBack = 0;
}
if( !CHK_ANY(input, IN_FORWARD) || CHK_ANY(input, IN_BACK) ) {
holdForward = -12;
} else if( CHK_ANY(input, IN_FORWARD) && ++holdForward >= 3 ) {
result |= IN_FORWARD;
holdForward = 0;
}
#endif // FEATURE_INPUT_IMPROVED
oldInput = input;
return result;
}
void __cdecl DoInventoryPicture() {
S_CopyBufferToScreen();
}
void __cdecl DoInventoryBackground() {
VECTOR_ANGLES angles;
PHD_3DPOS viewPos;
UINT16 *ptr;
S_CopyBufferToScreen();
if( Objects[ID_INV_BACKGROUND].loaded ) {
// set view
phd_GetVectorAngles(0, 0x1000, 0, &angles);
viewPos.x = 0;
viewPos.y = -0x200;
viewPos.z = 0;
viewPos.rotX = angles.pitch;
viewPos.rotY = angles.yaw;
viewPos.rotZ = 0;
phd_GenerateW2V(&viewPos);
// set lighting
LsDivider = 0x6000;
phd_GetVectorAngles(-0x600, 0x100, 0x400, &angles);
phd_RotateLight(angles.pitch, angles.yaw);
// transform and insert the mesh
phd_PushMatrix();
ptr = (UINT16 *)&Anims[Objects[ID_INV_BACKGROUND].animIndex].framePtr[9];
phd_TranslateAbs(0, 0x3000, 0);
phd_RotYXZ(0, PHD_90, PHD_180);
phd_RotYXZsuperpack(&ptr, 0);
phd_RotYXZ(PHD_180, 0, 0);
S_InsertInvBgnd(MeshPtr[Objects[ID_INV_BACKGROUND].meshIndex]);
phd_PopMatrix();
}
}
/*
* Inject function
*/
void Inject_Inventory() {
INJECT(0x00422060, Display_Inventory);
INJECT(0x004232F0, Construct_Inventory);
INJECT(0x00423450, SelectMeshes);
INJECT(0x004234E0, AnimateInventoryItem);
INJECT(0x00423570, DrawInventoryItem);
INJECT(0x004239A0, GetDebouncedInput);
INJECT(0x004239C0, DoInventoryPicture);
INJECT(0x004239D0, DoInventoryBackground);
}
================================================
FILE: game/inventory.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef INVENTORY_H_INCLUDED
#define INVENTORY_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
int __cdecl Display_Inventory(INVENTORY_MODE invMode); // 0x00422060
void __cdecl Construct_Inventory(); // 0x004232F0
void __cdecl SelectMeshes(INVENTORY_ITEM *invItem); // 0x00423450
int __cdecl AnimateInventoryItem(INVENTORY_ITEM *invItem); // 0x004234E0
void __cdecl DrawInventoryItem(INVENTORY_ITEM *invItem); // 0x00423570
DWORD __cdecl GetDebouncedInput(DWORD input); // 0x004239A0
void __cdecl DoInventoryPicture(); //0x004239C0
void __cdecl DoInventoryBackground(); // 0x004239D0
#endif // INVENTORY_H_INCLUDED
================================================
FILE: game/invfunc.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/invfunc.h"
#include "3dsystem/3d_gen.h"
#include "game/health.h"
#include "game/items.h"
#include "game/text.h"
#include "specific/frontend.h"
#include "global/vars.h"
// X coordinates relative to the center of the screen
// Y coordinates relative to the bottom of the screen
#define ITEMNAME_X_POS (0)
#define ITEMNAME_Y_POS (-16)
#define ITEMCOUNT_X_POS (64)
#define ITEMCOUNT_Y_POS (-56)
void __cdecl InitColours() {
InvColours[ICLR_Black] = S_COLOUR(0x00, 0x00, 0x00);
InvColours[ICLR_Gray] = S_COLOUR(0x40, 0x40, 0x40);
InvColours[ICLR_White] = S_COLOUR(0xFF, 0xFF, 0xFF);
InvColours[ICLR_Red] = S_COLOUR(0xFF, 0x00, 0x00);
InvColours[ICLR_Orange] = S_COLOUR(0xFF, 0x80, 0x00);
InvColours[ICLR_Yellow] = S_COLOUR(0xFF, 0xFF, 0x00);
InvColours[ICLR_DarkGreen] = S_COLOUR(0x00, 0x80, 0x00);
InvColours[ICLR_Green] = S_COLOUR(0x00, 0xFF, 0x00);
InvColours[ICLR_Cyan] = S_COLOUR(0x00, 0xFF, 0xFF);
InvColours[ICLR_Blue] = S_COLOUR(0x00, 0x00, 0xFF);
InvColours[ICLR_Magenta] = S_COLOUR(0xFF, 0x00, 0xFF);
}
void __cdecl RingIsOpen(RING_INFO *ring) {
GAME_STRING_ID strID;
if( InventoryMode == INV_TitleMode )
return;
if( InvRingText == NULL ) {
switch( ring->type ) {
case RING_Main :
strID = GSI_Heading_Inventory;
break;
case RING_Option :
strID = (InventoryMode == INV_DeathMode) ? GSI_Heading_GameOver : GSI_Heading_Option;
break;
case RING_Keys :
strID = GSI_Heading_Items;
break;
default :
return;
}
InvRingText = T_Print(0, 26, 0, GF_GameStringTable[strID]);
T_CentreH(InvRingText, 1);
}
if( InventoryMode == INV_KeysMode || InventoryMode == INV_DeathMode )
return;
if( InvUpArrow1 == NULL ) {
if( ring->type == RING_Option || (ring->type == RING_Main && InvKeyObjectsCount != 0) ) {
InvUpArrow1 = T_Print(20, 28, 0, "["); // '[' is Up Arrow Sprite here
InvUpArrow2 = T_Print(-20, 28, 0, "[");
T_RightAlign(InvUpArrow2, 1);
}
}
if( InvDownArrow1 == NULL ) {
if( ring->type == RING_Keys || (ring->type == RING_Main && !CHK_ANY(GF_GameFlow.flags, GFF_LockoutOptionRing)) ) {
InvDownArrow1 = T_Print(20, -15, 0, "]"); // ']' is Down Arrow Sprite here
InvDownArrow2 = T_Print(-20, -15, 0, "]");
T_BottomAlign(InvDownArrow1, 1);
T_BottomAlign(InvDownArrow2, 1);
T_RightAlign(InvDownArrow2, 1);
}
}
}
void __cdecl RingIsNotOpen() {
T_RemovePrint(InvRingText);
InvRingText = NULL;
T_RemovePrint(InvUpArrow1);
InvUpArrow1 = NULL;
T_RemovePrint(InvUpArrow2);
InvUpArrow2 = NULL;
T_RemovePrint(InvDownArrow1);
InvDownArrow1 = NULL;
T_RemovePrint(InvDownArrow2);
InvDownArrow2 = NULL;
}
void __cdecl RingNotActive(INVENTORY_ITEM *item) {
int itemCount;
char strBuf[64];
// Keys
if( InvItemText[0] == NULL ) {
switch( item->objectID ) {
case ID_PASSPORT_OPTION:
break;
case ID_PUZZLE_OPTION1:
InvItemText[0] = T_Print(ITEMNAME_X_POS, ITEMNAME_Y_POS, 0, GF_Puzzle1StringTable[CurrentLevel]);
break;
case ID_PUZZLE_OPTION2:
InvItemText[0] = T_Print(ITEMNAME_X_POS, ITEMNAME_Y_POS, 0, GF_Puzzle2StringTable[CurrentLevel]);
break;
case ID_PUZZLE_OPTION3:
InvItemText[0] = T_Print(ITEMNAME_X_POS, ITEMNAME_Y_POS, 0, GF_Puzzle3StringTable[CurrentLevel]);
break;
case ID_PUZZLE_OPTION4:
InvItemText[0] = T_Print(ITEMNAME_X_POS, ITEMNAME_Y_POS, 0, GF_Puzzle4StringTable[CurrentLevel]);
break;
case ID_PICKUP_OPTION1:
InvItemText[0] = T_Print(ITEMNAME_X_POS, ITEMNAME_Y_POS, 0, GF_Pickup1StringTable[CurrentLevel]);
break;
case ID_PICKUP_OPTION2:
InvItemText[0] = T_Print(ITEMNAME_X_POS, ITEMNAME_Y_POS, 0, GF_Pickup2StringTable[CurrentLevel]);
break;
case ID_KEY_OPTION1:
InvItemText[0] = T_Print(ITEMNAME_X_POS, ITEMNAME_Y_POS, 0, GF_Key1StringTable[CurrentLevel]);
break;
case ID_KEY_OPTION2:
InvItemText[0] = T_Print(ITEMNAME_X_POS, ITEMNAME_Y_POS, 0, GF_Key2StringTable[CurrentLevel]);
break;
case ID_KEY_OPTION3:
InvItemText[0] = T_Print(ITEMNAME_X_POS, ITEMNAME_Y_POS, 0, GF_Key3StringTable[CurrentLevel]);
break;
case ID_KEY_OPTION4:
InvItemText[0] = T_Print(ITEMNAME_X_POS, ITEMNAME_Y_POS, 0, GF_Key4StringTable[CurrentLevel]);
break;
default:
InvItemText[0] = T_Print(ITEMNAME_X_POS, ITEMNAME_Y_POS, 0, item->lpString);
break;
}
if( InvItemText[0] != NULL ) {
T_BottomAlign(InvItemText[0], 1);
T_CentreH(InvItemText[0], 1);
}
}
// Normal items
itemCount = Inv_RequestItem((GAME_OBJECT_ID)item->objectID);
switch( item->objectID ) {
case ID_PISTOL_OPTION:
return;
case ID_SHOTGUN_OPTION:
if( InvItemText[1] != NULL || SaveGame.bonusFlag ) {
return;
}
sprintf(strBuf, "%5d", (int)(Lara.shotgun_ammo / 6));
break;
case ID_MAGNUM_OPTION:
if( InvItemText[1] != NULL || SaveGame.bonusFlag ) {
return;
}
sprintf(strBuf, "%5d", (int)Lara.magnum_ammo);
break;
case ID_UZI_OPTION:
if( InvItemText[1] != NULL || SaveGame.bonusFlag ) {
return;
}
sprintf(strBuf, "%5d", (int)Lara.uzi_ammo);
break;
case ID_HARPOON_OPTION:
if( InvItemText[1] != NULL || SaveGame.bonusFlag ) {
return;
}
sprintf(strBuf, "%5d", (int)Lara.harpoon_ammo);
break;
case ID_M16_OPTION:
if( InvItemText[1] != NULL || SaveGame.bonusFlag ) {
return;
}
sprintf(strBuf, "%5d", (int)Lara.m16_ammo);
break;
case ID_GRENADE_OPTION:
if( InvItemText[1] != NULL || SaveGame.bonusFlag ) {
return;
}
sprintf(strBuf, "%5d", (int)Lara.grenade_ammo);
break;
case ID_PISTOL_AMMO_OPTION:
return;
case ID_SHOTGUN_AMMO_OPTION:
if( InvItemText[1] != NULL || SaveGame.bonusFlag ) {
return;
}
sprintf(strBuf, "%d", itemCount * 2);
break;
case ID_MAGNUM_AMMO_OPTION:
case ID_UZI_AMMO_OPTION:
case ID_HARPOON_AMMO_OPTION:
case ID_M16_AMMO_OPTION:
if( InvItemText[1] != NULL || SaveGame.bonusFlag ) {
return;
}
sprintf(strBuf, "%d", itemCount * 2);
break;
case ID_GRENADE_AMMO_OPTION:
case ID_FLARES_OPTION:
if( InvItemText[1] != NULL || SaveGame.bonusFlag ) {
return;
}
sprintf(strBuf, "%d", itemCount);
break;
case ID_SMALL_MEDIPACK_OPTION:
case ID_LARGE_MEDIPACK_OPTION:
HealthBarTimer = 40; // 1.33 seconds
DrawHealthBar(FlashIt());
// fall through
case ID_PUZZLE_OPTION1:
case ID_PUZZLE_OPTION2:
case ID_PUZZLE_OPTION3:
case ID_PUZZLE_OPTION4:
case ID_KEY_OPTION1:
case ID_KEY_OPTION2:
case ID_KEY_OPTION3:
case ID_KEY_OPTION4:
case ID_PICKUP_OPTION1:
case ID_PICKUP_OPTION2:
if( InvItemText[1] != NULL || itemCount <= 1 ) {
return;
}
sprintf(strBuf, "%d", itemCount);
break;
default:
return;
}
if( InvItemText[1] == NULL ) {
MakeAmmoString(strBuf);
InvItemText[1] = T_Print(ITEMCOUNT_X_POS, ITEMCOUNT_Y_POS, 0, strBuf);
T_BottomAlign(InvItemText[1], 1);
T_CentreH(InvItemText[1], 1);
}
}
void __cdecl RingActive() {
T_RemovePrint(InvItemText[0]);
InvItemText[0] = NULL;
T_RemovePrint(InvItemText[1]);
InvItemText[1] = NULL;
}
BOOL __cdecl Inv_AddItem(GAME_OBJECT_ID itemID) {
int i, j, items;
int found = 0;
GAME_OBJECT_ID optionID = Inv_GetItemOption(itemID);
for( i = 0; i < InvMainObjectsCount; ++i ) {
if( InvMainList[i]->objectID == optionID ) {
found = 1;
break;
}
}
for( j = 0; j < InvKeyObjectsCount; ++j ) {
if( InvKeysList[j]->objectID == optionID ) {
found = 2;
break;
}
}
if( found == 1 ) {
if( itemID == ID_FLARES_ITEM )
InvMainQtys[i] += 6;
else
++InvMainQtys[i];
return TRUE;
}
if( found == 2 ) {
++InvKeysQtys[j];
return TRUE;
}
switch( itemID ) {
case ID_COMPASS_ITEM :
case ID_COMPASS_OPTION :
Inv_InsertItem(&InvCompassOption);
return TRUE;
case ID_PISTOL_ITEM :
case ID_PISTOL_OPTION :
Inv_InsertItem(&InvPistolOption);
if( Lara.last_gun_type == LGT_Unarmed )
Lara.last_gun_type = LGT_Pistols;
return TRUE;
case ID_SHOTGUN_ITEM :
case ID_SHOTGUN_OPTION :
items = Inv_RequestItem(ID_SHOTGUN_AMMO_ITEM);
for( i = 0; i < items; ++i ) {
Inv_RemoveItem(ID_SHOTGUN_AMMO_ITEM);
Lara.shotgun_ammo += 12;
}
Lara.shotgun_ammo += 12;
Inv_InsertItem(&InvShotgunOption);
if( Lara.last_gun_type == LGT_Unarmed )
Lara.last_gun_type = LGT_Shotgun;
if( Lara.back_gun == ID_LARA )
Lara.back_gun = ID_LARA_SHOTGUN;
GlobalItemReplace(ID_SHOTGUN_ITEM, ID_SHOTGUN_AMMO_ITEM);
return FALSE;
case ID_MAGNUM_ITEM :
case ID_MAGNUM_OPTION :
items = Inv_RequestItem(ID_MAGNUM_AMMO_ITEM);
for( i = 0; i < items; ++i ) {
Inv_RemoveItem(ID_MAGNUM_AMMO_ITEM);
Lara.magnum_ammo += 40;
}
Lara.magnum_ammo += 40;
Inv_InsertItem(&InvMagnumOption);
GlobalItemReplace(ID_MAGNUM_ITEM, ID_MAGNUM_AMMO_ITEM);
return FALSE;
case ID_UZI_ITEM :
case ID_UZI_OPTION :
items = Inv_RequestItem(ID_UZI_AMMO_ITEM);
for( i = 0; i < items; ++i ) {
Inv_RemoveItem(ID_UZI_AMMO_ITEM);
Lara.uzi_ammo += 80;
}
Lara.uzi_ammo += 80;
Inv_InsertItem(&InvUziOption);
GlobalItemReplace(ID_UZI_ITEM, ID_UZI_AMMO_ITEM);
return FALSE;
case ID_HARPOON_ITEM :
case ID_HARPOON_OPTION :
items = Inv_RequestItem(ID_HARPOON_AMMO_ITEM);
for( i = 0; i < items; ++i ) {
Inv_RemoveItem(ID_HARPOON_AMMO_ITEM);
Lara.harpoon_ammo += 3;
}
Lara.harpoon_ammo += 3;
Inv_InsertItem(&InvHarpoonOption);
GlobalItemReplace(ID_HARPOON_ITEM, ID_HARPOON_AMMO_ITEM);
return FALSE;
case ID_M16_ITEM :
case ID_M16_OPTION :
items = Inv_RequestItem(ID_M16_AMMO_ITEM);
for( i = 0; i < items; ++i ) {
Inv_RemoveItem(ID_M16_AMMO_ITEM);
Lara.m16_ammo += 40;
}
Lara.m16_ammo += 40;
Inv_InsertItem(&InvM16Option);
GlobalItemReplace(ID_M16_ITEM, ID_M16_AMMO_ITEM);
return FALSE;
case ID_GRENADE_ITEM :
case ID_GRENADE_OPTION :
items = Inv_RequestItem(ID_GRENADE_AMMO_ITEM);
for( i = 0; i < items; ++i ) {
Inv_RemoveItem(ID_GRENADE_AMMO_ITEM);
Lara.grenade_ammo += 2;
}
Lara.grenade_ammo += 2;
Inv_InsertItem(&InvGrenadeOption);
GlobalItemReplace(ID_GRENADE_ITEM, ID_GRENADE_AMMO_ITEM);
return FALSE;
case ID_SHOTGUN_AMMO_ITEM :
case ID_SHOTGUN_AMMO_OPTION :
if( Inv_RequestItem(ID_SHOTGUN_ITEM) != 0 )
Lara.shotgun_ammo += 12;
else
Inv_InsertItem(&InvShotgunAmmoOption);
return FALSE;
case ID_MAGNUM_AMMO_ITEM :
case ID_MAGNUM_AMMO_OPTION :
if( Inv_RequestItem(ID_MAGNUM_ITEM) != 0 )
Lara.magnum_ammo += 40;
else
Inv_InsertItem(&InvMagnumAmmoOption);
return FALSE;
case ID_UZI_AMMO_ITEM :
case ID_UZI_AMMO_OPTION :
if( Inv_RequestItem(ID_UZI_ITEM) != 0 )
Lara.uzi_ammo += 80;
else
Inv_InsertItem(&InvUziAmmoOption);
return FALSE;
case ID_HARPOON_AMMO_ITEM :
case ID_HARPOON_AMMO_OPTION :
if( Inv_RequestItem(ID_HARPOON_ITEM) != 0 )
Lara.harpoon_ammo += 3;
else
Inv_InsertItem(&InvHarpoonAmmoOption);
return FALSE;
case ID_M16_AMMO_ITEM :
case ID_M16_AMMO_OPTION :
if( Inv_RequestItem(ID_M16_ITEM) != 0 )
Lara.m16_ammo += 40;
else
Inv_InsertItem(&InvM16AmmoOption);
return FALSE;
case ID_GRENADE_AMMO_ITEM :
case ID_GRENADE_AMMO_OPTION :
if( Inv_RequestItem(ID_GRENADE_ITEM) != 0 )
Lara.grenade_ammo += 2;
else
Inv_InsertItem(&InvGrenadeAmmoOption);
return FALSE;
case ID_FLARES_ITEM :
case ID_FLARES_OPTION :
Inv_InsertItem(&InvFlareOption);
for( i=0; i<5; ++i ) {
Inv_AddItem(ID_FLARE_ITEM);
}
return TRUE;
case ID_FLARE_ITEM :
Inv_InsertItem(&InvFlareOption);
return TRUE;
case ID_SMALL_MEDIPACK_ITEM :
case ID_SMALL_MEDIPACK_OPTION :
Inv_InsertItem(&InvSmallMedipackOption);
return TRUE;
case ID_LARGE_MEDIPACK_ITEM :
case ID_LARGE_MEDIPACK_OPTION :
Inv_InsertItem(&InvLargeMedipackOption);
return TRUE;
case ID_SECRET1 :
SaveGame.statistics.secrets |= 1;
return TRUE;
case ID_SECRET2 :
SaveGame.statistics.secrets |= 2;
return TRUE;
case ID_SECRET3 :
SaveGame.statistics.secrets |= 4;
return TRUE;
case ID_PICKUP_ITEM1 :
case ID_PICKUP_OPTION1 :
Inv_InsertItem(&InvPickup1Option);
return TRUE;
case ID_PICKUP_ITEM2 :
case ID_PICKUP_OPTION2 :
Inv_InsertItem(&InvPickup2Option);
return TRUE;
case ID_PUZZLE_ITEM1 :
case ID_PUZZLE_OPTION1 :
Inv_InsertItem(&InvPuzzle1Option);
return TRUE;
case ID_PUZZLE_ITEM2 :
case ID_PUZZLE_OPTION2 :
Inv_InsertItem(&InvPuzzle2Option);
return TRUE;
case ID_PUZZLE_ITEM3 :
case ID_PUZZLE_OPTION3 :
Inv_InsertItem(&InvPuzzle3Option);
return TRUE;
case ID_PUZZLE_ITEM4 :
case ID_PUZZLE_OPTION4 :
Inv_InsertItem(&InvPuzzle4Option);
return TRUE;
case ID_KEY_ITEM1 :
case ID_KEY_OPTION1 :
Inv_InsertItem(&InvKey1Option);
return TRUE;
case ID_KEY_ITEM2 :
case ID_KEY_OPTION2 :
Inv_InsertItem(&InvKey2Option);
return TRUE;
case ID_KEY_ITEM3 :
case ID_KEY_OPTION3 :
Inv_InsertItem(&InvKey3Option);
return TRUE;
case ID_KEY_ITEM4 :
case ID_KEY_OPTION4 :
Inv_InsertItem(&InvKey4Option);
return TRUE;
default:
break;
}
return FALSE;
}
void __cdecl Inv_InsertItem(INVENTORY_ITEM *item) {
int i, j;
if( item->invPos < 100 ) {
for( i = 0; i < InvMainObjectsCount; ++i ) {
if( InvMainList[i]->invPos > item->invPos ) {
for( j = InvMainObjectsCount; j >= i; --j ) {
InvMainList[j+1] = InvMainList[j];
InvMainQtys[j+1] = InvMainQtys[j];
}
break;
}
}
InvMainList[i] = item;
InvMainQtys[i] = 1;
++InvMainObjectsCount;
} else {
for( i = 0; i < InvKeyObjectsCount; ++i ) {
if( InvKeysList[i]->invPos > item->invPos ) {
for( j = InvKeyObjectsCount; j >= i; --j ) {
InvKeysList[j+1] = InvKeysList[j];
InvKeysQtys[j+1] = InvKeysQtys[j];
}
break;
}
}
InvKeysList[i] = item;
InvKeysQtys[i] = 1;
++InvKeyObjectsCount;
}
}
int __cdecl Inv_RequestItem(GAME_OBJECT_ID itemID) {
int i;
int optionID = Inv_GetItemOption(itemID);
for( i = 0; i < InvMainObjectsCount; ++i )
if( InvMainList[i]->objectID == optionID )
return InvMainQtys[i];
for( i = 0; i < InvKeyObjectsCount; ++i )
if( InvKeysList[i]->objectID == optionID )
return InvKeysQtys[i];
return 0;
}
void __cdecl Inv_RemoveAllItems() {
InvMainObjectsCount = 0;
InvMainCurrent = 0;
InvKeyObjectsCount = 0;
InvKeysCurrent = 0;
}
BOOL __cdecl Inv_RemoveItem(GAME_OBJECT_ID itemID) {
int i, j;
GAME_OBJECT_ID optionID = Inv_GetItemOption(itemID);
for( i = 0; i < InvMainObjectsCount; ++i ) {
if( InvMainList[i]->objectID == optionID ) {
if( --InvMainQtys[i] <= 0 ) {
--InvMainObjectsCount;
for( j = i; j < InvMainObjectsCount; ++j ) {
InvMainList[j] = InvMainList[j+1];
InvMainQtys[j] = InvMainQtys[j+1];
}
}
return TRUE;
}
}
for( i = 0; i < InvKeyObjectsCount; ++i ) {
if( InvKeysList[i]->objectID == optionID ) {
if( --InvKeysQtys[i] <= 0 ) {
--InvKeyObjectsCount;
for( j = i; j < InvKeyObjectsCount; ++j ) {
InvKeysList[j] = InvKeysList[j+1];
InvKeysQtys[j] = InvKeysQtys[j+1];
}
}
return TRUE;
}
}
return FALSE;
}
GAME_OBJECT_ID __cdecl Inv_GetItemOption(GAME_OBJECT_ID itemID) {
switch( itemID ) {
// Weapons
case ID_PISTOL_ITEM :
case ID_PISTOL_OPTION :
return ID_PISTOL_OPTION;
case ID_SHOTGUN_ITEM :
case ID_SHOTGUN_OPTION :
return ID_SHOTGUN_OPTION;
case ID_MAGNUM_ITEM :
case ID_MAGNUM_OPTION :
return ID_MAGNUM_OPTION;
case ID_UZI_ITEM :
case ID_UZI_OPTION :
return ID_UZI_OPTION;
case ID_HARPOON_ITEM :
case ID_HARPOON_OPTION :
return ID_HARPOON_OPTION;
case ID_M16_ITEM :
case ID_M16_OPTION :
return ID_M16_OPTION;
case ID_GRENADE_ITEM :
case ID_GRENADE_OPTION :
return ID_GRENADE_OPTION;
// Ammo
// NOTE: It seems here could be Pistol Ammo Option, but it was deleted or commented by CORE
/* case ID_PISTOL_AMMO_ITEM :
case ID_PISTOL_AMMO_OPTION :
return ID_PISTOL_AMMO_OPTION; */
case ID_SHOTGUN_AMMO_ITEM :
case ID_SHOTGUN_AMMO_OPTION :
return ID_SHOTGUN_AMMO_OPTION;
case ID_MAGNUM_AMMO_ITEM :
case ID_MAGNUM_AMMO_OPTION :
return ID_MAGNUM_AMMO_OPTION;
case ID_UZI_AMMO_ITEM :
case ID_UZI_AMMO_OPTION :
return ID_UZI_AMMO_OPTION;
case ID_HARPOON_AMMO_ITEM :
case ID_HARPOON_AMMO_OPTION :
return ID_HARPOON_AMMO_OPTION;
case ID_M16_AMMO_ITEM :
case ID_M16_AMMO_OPTION :
return ID_M16_AMMO_OPTION;
case ID_GRENADE_AMMO_ITEM :
case ID_GRENADE_AMMO_OPTION :
return ID_GRENADE_AMMO_OPTION;
// Flares, Medipacks
case ID_SMALL_MEDIPACK_ITEM :
case ID_SMALL_MEDIPACK_OPTION :
return ID_SMALL_MEDIPACK_OPTION;
case ID_LARGE_MEDIPACK_ITEM :
case ID_LARGE_MEDIPACK_OPTION :
return ID_LARGE_MEDIPACK_OPTION;
case ID_FLARE_ITEM :
case ID_FLARES_ITEM :
case ID_FLARES_OPTION :
return ID_FLARES_OPTION;
// Puzzles
case ID_PUZZLE_ITEM1 :
case ID_PUZZLE_OPTION1 :
return ID_PUZZLE_OPTION1;
case ID_PUZZLE_ITEM2 :
case ID_PUZZLE_OPTION2 :
return ID_PUZZLE_OPTION2;
case ID_PUZZLE_ITEM3 :
case ID_PUZZLE_OPTION3 :
return ID_PUZZLE_OPTION3;
case ID_PUZZLE_ITEM4 :
case ID_PUZZLE_OPTION4 :
return ID_PUZZLE_OPTION4;
// Pickups
case ID_PICKUP_ITEM1 :
case ID_PICKUP_OPTION1 :
return ID_PICKUP_OPTION1;
case ID_PICKUP_ITEM2 :
case ID_PICKUP_OPTION2 :
return ID_PICKUP_OPTION2;
// Keys
case ID_KEY_ITEM1 :
case ID_KEY_OPTION1 :
return ID_KEY_OPTION1;
case ID_KEY_ITEM2 :
case ID_KEY_OPTION2 :
return ID_KEY_OPTION2;
case ID_KEY_ITEM3 :
case ID_KEY_OPTION3 :
return ID_KEY_OPTION3;
case ID_KEY_ITEM4 :
case ID_KEY_OPTION4 :
return ID_KEY_OPTION4;
default :
break;
}
return ID_NONE;
}
void __cdecl RemoveInventoryText() {
for( int i=0; i<2; ++i ) {
T_RemovePrint(InvItemText[i]);
InvItemText[i] = NULL;
}
}
void __cdecl Inv_RingInit(RING_INFO *ring, __int16 type, INVENTORY_ITEM **itemList, __int16 objCount, __int16 currentObj, INV_MOTION_INFO *motionInfo) {
ring->type = type;
ring->itemList = itemList;
ring->objCount = objCount;
ring->currentObj = currentObj;
ring->radius = 0;
ring->angleAdder = PHD_360/ objCount;
ring->cameraPitch = ( InventoryMode == INV_TitleMode ) ? 0x400 : 0;
ring->isRotating = 0;
ring->rotCount = 0;
ring->targetObj = 0;
ring->rotAdder = 0;
ring->rotAdderL = 0;
ring->rotAdderR = 0;
ring->motionInfo = motionInfo;
ring->camera.x = 0;
ring->camera.y = -0x600;
ring->camera.z = 0x380;
ring->camera.rotX = 0;
ring->camera.rotY = 0;
ring->camera.rotZ = 0;
Inv_RingMotionInit(ring, 32, 0, 1);
Inv_RingMotionRadius(ring, 0x2B0);
Inv_RingMotionCameraPos(ring, -0x100);
Inv_RingMotionRotation(ring, PHD_180, (-PHD_90) - (ring->currentObj * ring->angleAdder));
ring->ringPos.x = 0;
ring->ringPos.y = 0;
ring->ringPos.z = 0;
ring->ringPos.rotX = 0;
ring->ringPos.rotZ = 0;
ring->ringPos.rotY = motionInfo->rotateTarget + PHD_180;
ring->light.x = -0x600;
ring->light.y = 0x100;
ring->light.z = 0x400;
}
void __cdecl Inv_RingGetView(RING_INFO *ring, PHD_3DPOS *view) {
VECTOR_ANGLES angles;
phd_GetVectorAngles((0 - ring->camera.x), (-96 - ring->camera.y), (ring->radius - ring->camera.z), &angles);
view->x = ring->camera.x;
view->y = ring->camera.y;
view->z = ring->camera.z;
view->rotX = angles.pitch + ring->cameraPitch;
view->rotY = angles.yaw;
view->rotZ = 0;
}
void __cdecl Inv_RingLight(RING_INFO *ring) {
VECTOR_ANGLES angles;
LsDivider = 0x6000;
phd_GetVectorAngles(ring->light.x, ring->light.y, ring->light.z, &angles);
phd_RotateLight(angles.pitch, angles.yaw);
}
void __cdecl Inv_RingCalcAdders(RING_INFO *ring, __int16 rotDuration) {
ring->angleAdder = PHD_360 / ring->objCount;
ring->rotAdderL = ring->angleAdder / rotDuration;
ring->rotAdderR = -ring->rotAdderL;
}
void __cdecl Inv_RingDoMotions(RING_INFO *ring) {
INVENTORY_ITEM *item;
INV_MOTION_INFO *mi = ring->motionInfo;
if( mi->framesCount != 0 ) {
ring->radius += mi->radiusRate;
ring->camera.y += mi->cameraRate_y;
ring->ringPos.rotY += mi->rotateRate;
ring->cameraPitch += mi->cameraRate_pitch;
item = ring->itemList[ring->currentObj];
item->xRotPt += mi->itemRate_xRotPt;
item->yRotSel += mi->itemRate_xRot;
item->yTrans += mi->itemRate_yTrans;
item->zTrans += mi->itemRate_zTrans;
if( --mi->framesCount == 0 ) {
mi->status = mi->statusTarget;
if( mi->radiusRate != 0 ) {
mi->radiusRate = 0;
ring->radius = mi->radiusTarget;
}
if( mi->cameraRate_y != 0 ) {
mi->cameraRate_y = 0;
ring->camera.y = mi->cameraTarget_y;
}
if( mi->rotateRate != 0 ) {
mi->rotateRate = 0;
ring->ringPos.rotY = mi->rotateTarget;
}
if( mi->itemRate_xRotPt != 0 ) {
mi->itemRate_xRotPt = 0;
item->xRotPt = mi->itemTarget_xRotPt;
}
if( mi->itemRate_xRot != 0 ) {
mi->itemRate_xRot = 0;
item->yRotSel = mi->itemTarget_xRot;
}
if( mi->itemRate_yTrans != 0 ) {
mi->itemRate_yTrans = 0;
item->yTrans = mi->itemTarget_yTrans;
}
if( mi->itemRate_zTrans != 0 ) {
mi->itemRate_zTrans = 0;
item->zTrans = mi->itemTarget_zTrans;
}
if( mi->cameraRate_pitch != 0 ) {
mi->cameraRate_pitch = 0;
ring->cameraPitch = mi->cameraTarget_pitch;
}
}
}
if( ring->isRotating != 0 ) {
ring->ringPos.rotY += ring->rotAdder;
if( --ring->rotCount == 0 ) {
ring->currentObj = ring->targetObj;
ring->ringPos.rotY = (-PHD_90) - (ring->currentObj * ring->angleAdder);
ring->isRotating = 0;
}
}
}
void __cdecl Inv_RingRotateLeft(RING_INFO *ring) {
ring->isRotating = 1;
ring->targetObj = ring->currentObj - 1;
if( ring->targetObj < 0 ) {
ring->targetObj = ring->objCount - 1;
}
ring->rotCount = 24;
ring->rotAdder = ring->rotAdderL;
}
void __cdecl Inv_RingRotateRight(RING_INFO *ring) {
ring->isRotating = 1;
ring->targetObj = ring->currentObj + 1;
if( ring->targetObj >= ring->objCount ) {
ring->targetObj = 0;
}
ring->rotCount = 24;
ring->rotAdder = ring->rotAdderR;
}
void __cdecl Inv_RingMotionInit(RING_INFO *ring, __int16 framesCount, __int16 status, __int16 statusTarget) {
INV_MOTION_INFO *mi = ring->motionInfo;
mi->framesCount = framesCount;
mi->status = status;
mi->statusTarget = statusTarget;
mi->radiusTarget = 0;
mi->radiusRate = 0;
mi->cameraTarget_y = 0;
mi->cameraRate_y = 0;
mi->cameraTarget_pitch = 0;
mi->cameraRate_pitch = 0;
mi->rotateTarget = 0;
mi->rotateRate = 0;
mi->itemTarget_xRotPt = 0;
mi->itemRate_xRotPt = 0;
mi->itemTarget_xRot = 0;
mi->itemRate_xRot = 0;
mi->itemTarget_yTrans = 0;
mi->itemRate_yTrans = 0;
mi->itemTarget_zTrans = 0;
mi->itemRate_zTrans = 0;
mi->misc = 0;
}
void __cdecl Inv_RingMotionSetup(RING_INFO *ring, __int16 status, __int16 statusTarget, __int16 framesCount) {
INV_MOTION_INFO *mi = ring->motionInfo;
mi->framesCount = framesCount;
mi->statusTarget = statusTarget;
mi->status = status;
mi->radiusRate = 0;
mi->cameraRate_y = 0;
}
void __cdecl Inv_RingMotionRadius(RING_INFO *ring, __int16 target) {
INV_MOTION_INFO *mi = ring->motionInfo;
mi->radiusTarget = target;
mi->radiusRate = (target - ring->radius) / mi->framesCount;
}
void __cdecl Inv_RingMotionRotation(RING_INFO *ring, __int16 rotation, __int16 target) {
INV_MOTION_INFO *mi = ring->motionInfo;
mi->rotateTarget = target;
mi->rotateRate = rotation / mi->framesCount;
}
void __cdecl Inv_RingMotionCameraPos(RING_INFO *ring, __int16 target) {
INV_MOTION_INFO *mi = ring->motionInfo;
mi->cameraTarget_y = target;
mi->cameraRate_y = (target - ring->camera.y) / mi->framesCount;
}
void __cdecl Inv_RingMotionCameraPitch(RING_INFO *ring, __int16 target) {
INV_MOTION_INFO *mi = ring->motionInfo;
mi->cameraTarget_pitch = target;
mi->cameraRate_pitch = target / mi->framesCount;
}
void __cdecl Inv_RingMotionItemSelect(RING_INFO *ring, INVENTORY_ITEM *item) {
INV_MOTION_INFO *mi = ring->motionInfo;
mi->itemTarget_xRotPt = item->xRotPtSel;
mi->itemRate_xRotPt = item->xRotPtSel / mi->framesCount;
mi->itemTarget_xRot = item->xRotSel;
mi->itemRate_xRot = (item->xRotSel - item->xRot) / mi->framesCount;
mi->itemTarget_yTrans = item->yTransSel;
mi->itemRate_yTrans = item->yTransSel / mi->framesCount;
mi->itemTarget_zTrans = item->zTransSel;
mi->itemRate_zTrans = item->zTransSel / mi->framesCount;
}
void __cdecl Inv_RingMotionItemDeselect(RING_INFO *ring, INVENTORY_ITEM *item) {
INV_MOTION_INFO *mi = ring->motionInfo;
mi->itemTarget_xRotPt = 0;
mi->itemRate_xRotPt = -(item->xRotPtSel / mi->framesCount);
mi->itemTarget_xRot = item->xRot;
mi->itemRate_xRot = (item->xRot - item->xRotSel) / mi->framesCount;
mi->itemTarget_yTrans = 0;
mi->itemRate_yTrans = -(item->yTransSel / mi->framesCount);
mi->itemTarget_zTrans = 0;
mi->itemRate_zTrans = -(item->zTransSel / mi->framesCount);
}
/*
* Inject function
*/
void Inject_InvFunc() {
INJECT(0x00423B10, InitColours);
INJECT(0x00423C20, RingIsOpen);
INJECT(0x00423D90, RingIsNotOpen);
INJECT(0x00423E20, RingNotActive);
INJECT(0x00424290, RingActive);
INJECT(0x004242D0, Inv_AddItem);
INJECT(0x00424AE0, Inv_InsertItem);
INJECT(0x00424C10, Inv_RequestItem);
INJECT(0x00424C90, Inv_RemoveAllItems);
INJECT(0x00424CB0, Inv_RemoveItem);
INJECT(0x00424DC0, Inv_GetItemOption);
INJECT(0x00424FB0, RemoveInventoryText);
INJECT(0x00424FE0, Inv_RingInit);
INJECT(0x004250F0, Inv_RingGetView);
INJECT(0x00425150, Inv_RingLight);
INJECT(0x00425190, Inv_RingCalcAdders);
INJECT(0x004251C0, Inv_RingDoMotions);
INJECT(0x00425300, Inv_RingRotateLeft);
INJECT(0x00425330, Inv_RingRotateRight);
INJECT(0x00425360, Inv_RingMotionInit);
INJECT(0x004253D0, Inv_RingMotionSetup);
INJECT(0x00425400, Inv_RingMotionRadius);
INJECT(0x00425430, Inv_RingMotionRotation);
INJECT(0x00425460, Inv_RingMotionCameraPos);
INJECT(0x00425490, Inv_RingMotionCameraPitch);
INJECT(0x004254B0, Inv_RingMotionItemSelect);
INJECT(0x00425510, Inv_RingMotionItemDeselect);
}
================================================
FILE: game/invfunc.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef INVFUNC_H_INCLUDED
#define INVFUNC_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl InitColours(); // 0x00423B10
void __cdecl RingIsOpen(RING_INFO *ring); // 0x00423C20
void __cdecl RingIsNotOpen(); // 0x00423D90
void __cdecl RingNotActive(INVENTORY_ITEM *item); // 0x00423E20
void __cdecl RingActive(); // 0x00424290
BOOL __cdecl Inv_AddItem(GAME_OBJECT_ID itemID); // 0x004242D0
void __cdecl Inv_InsertItem(INVENTORY_ITEM *item); // 0x00424AE0
int __cdecl Inv_RequestItem(GAME_OBJECT_ID itemID); // 0x00424C10
void __cdecl Inv_RemoveAllItems(); // 0x00424C90
BOOL __cdecl Inv_RemoveItem(GAME_OBJECT_ID itemID); // 0x00424CB0
GAME_OBJECT_ID __cdecl Inv_GetItemOption(GAME_OBJECT_ID itemID); // 0x00424DC0
void __cdecl RemoveInventoryText(); // 0x00424FB0
void __cdecl Inv_RingInit(RING_INFO *ring, __int16 type, INVENTORY_ITEM **itemList, __int16 objCount, __int16 currentObj, INV_MOTION_INFO *motionInfo); // 0x00424FE0
void __cdecl Inv_RingGetView(RING_INFO *ring, PHD_3DPOS *view); // 0x004250F0
void __cdecl Inv_RingLight(RING_INFO *ring); // 0x00425150
void __cdecl Inv_RingCalcAdders(RING_INFO *ring, __int16 rotDuration); // 0x00425190
void __cdecl Inv_RingDoMotions(RING_INFO *ring); // 0x004251C0
void __cdecl Inv_RingRotateLeft(RING_INFO *ring); // 0x00425300
void __cdecl Inv_RingRotateRight(RING_INFO *ring); // 0x00425330
void __cdecl Inv_RingMotionInit(RING_INFO *ring, __int16 framesCount, __int16 status, __int16 statusTarget); // 0x00425360
void __cdecl Inv_RingMotionSetup(RING_INFO *ring, __int16 status, __int16 statusTarget, __int16 framesCount); // 0x004253D0
void __cdecl Inv_RingMotionRadius(RING_INFO *ring, __int16 target); // 0x00425400
void __cdecl Inv_RingMotionRotation(RING_INFO *ring, __int16 rotation, __int16 target); // 0x00425430
void __cdecl Inv_RingMotionCameraPos(RING_INFO *ring, __int16 target); // 0x00425460
void __cdecl Inv_RingMotionCameraPitch(RING_INFO *ring, __int16 target); // 0x00425490
void __cdecl Inv_RingMotionItemSelect(RING_INFO *ring, INVENTORY_ITEM *item); // 0x004254B0
void __cdecl Inv_RingMotionItemDeselect(RING_INFO *ring, INVENTORY_ITEM *item); // 0x00425510
#endif // INVFUNC_H_INCLUDED
================================================
FILE: game/invtext.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/invtext.h"
#include "game/text.h"
#include "specific/output.h"
#include "specific/smain.h"
#include "global/vars.h"
#define REQ_NEARZ (8)
#define REQ_MIDZ (16)
#define REQ_FARZ (48)
#define STATS_LN_COUNT (7)
#define STATS_WIDTH (304)
// Y coordinates relative to the bottom of the screen
#ifdef FEATURE_HUD_IMPROVED
#define STATS_Y_POS (-44)
#define REQ_LN_HEIGHT (15)
extern DWORD InvTextBoxMode;
static const char MoreDownString[] = " \x0F \x0F ";
static const char MoreUpString[] = " \x10 \x10 ";
#else // FEATURE_HUD_IMPROVED
#define STATS_Y_POS (-32)
#define REQ_LN_HEIGHT (18)
#endif // FEATURE_HUD_IMPROVED
// NOTE: gouraud arrays have been taken from PlayStation version of the game.
// These arrays are not used in the original PC version of the game.
GOURAUD_FILL ReqBgndGour1 = {{
{0x80002000, 0x80002000, 0x80006000, 0x80002000},
{0x80002000, 0x80002000, 0x80002000, 0x80006000},
{0x80006000, 0x80002000, 0x80002000, 0x80002000},
{0x80002000, 0x80006000, 0x80002000, 0x80002000},
}};
GOURAUD_OUTLINE ReqBgndGour2 = {
0xFF606060, 0xFF808080, 0xFF202020,
0xFF000000, 0xFF000000, 0xFF202020,
0xFF404040, 0xFF404040, 0xFF606060,
};
GOURAUD_FILL ReqMainGour1 = {{
{0x80000000, 0x80000000, 0x80108038, 0x80000000},
{0x80000000, 0x80000000, 0x80000000, 0x80108038},
{0x80108038, 0x80000000, 0x80000000, 0x80000000},
{0x80000000, 0x80108038, 0x80000000, 0x80000000},
}};
GOURAUD_OUTLINE ReqMainGour2 = {
0xFF000000, 0xFF000000, 0xFF000000,
0xFF000000, 0xFF000000, 0xFF000000,
0xFF000000, 0xFF000000, 0xFF000000,
};
GOURAUD_FILL ReqSelGour1 = {{
{0x80000000, 0x80000000, 0x8038F080, 0x80000000},
{0x80000000, 0x80000000, 0x80000000, 0x8038F080},
{0x8038F080, 0x80000000, 0x80000000, 0x80000000},
{0x80000000, 0x8038F080, 0x80000000, 0x80000000},
}};
GOURAUD_OUTLINE ReqSelGour2 = {
0xFF000000, 0xFFFFFFFF, 0xFF000000,
0xFF38F080, 0xFF000000, 0xFFFFFFFF,
0xFF000000, 0xFF38F080, 0xFF000000,
};
extern int GF_GetNumSecrets(DWORD levelID);
#ifdef FEATURE_ASSAULT_SAVE
extern void SaveAssault();
#endif // FEATURE_ASSAULT_SAVE
void __cdecl Init_Requester(REQUEST_INFO *req) {
req->headingText1 = NULL;
req->headingText2 = NULL;
req->headingFlags1 = 0;
req->headingFlags2 = 0;
req->backgroundText = NULL;
req->backgroundFlags = 1;
req->moreupText = NULL;
req->moreupFlags = 1;
req->moredownText = NULL;
req->moredownFlags = 1;
for( int i=0; i<24; ++i ) {
req->itemTexts1[i] = NULL;
req->itemFlags1[i] = 0;
req->itemTexts2[i] = NULL;
req->itemFlags2[i] = 0;
}
req->itemsCount = 0;
req->lpItemFlags1 = RequesterItemFlags1;
req->lpItemFlags2 = RequesterItemFlags2;
#ifdef FEATURE_HUD_IMPROVED
req->renderWidth = GetRenderWidthDownscaled();
req->renderHeight = GetRenderHeightDownscaled();
#else // !FEATURE_HUD_IMPROVED
req->renderWidth = GetRenderWidth();
req->renderHeight = GetRenderHeight();
#endif // FEATURE_HUD_IMPROVED
}
void __cdecl Remove_Requester(REQUEST_INFO *req) {
T_RemovePrint(req->headingText1);
req->headingText1 = NULL;
T_RemovePrint(req->headingText2);
req->headingText2 = NULL;
T_RemovePrint(req->backgroundText);
req->backgroundText = NULL;
T_RemovePrint(req->moreupText);
req->moreupText = NULL;
T_RemovePrint(req->moredownText);
req->moredownText = NULL;
for( int i=0; i<24; ++i ) {
T_RemovePrint(req->itemTexts1[i]);
req->itemTexts1[i] = NULL;
T_RemovePrint(req->itemTexts2[i]);
req->itemTexts2[i] = NULL;
}
}
void __cdecl ReqItemCentreAlign(REQUEST_INFO *req, TEXT_STR_INFO *textInfo) {
if( textInfo != NULL ) {
textInfo->xPos = req->xPos;
textInfo->bgndOffX = 0;
}
}
void __cdecl ReqItemLeftAlign(REQUEST_INFO *req, TEXT_STR_INFO *textInfo) {
int bgndOffX;
if( textInfo == NULL )
return;
#ifdef FEATURE_HUD_IMPROVED
bgndOffX = (req->pixWidth - T_GetTextWidth(textInfo)) / 2 - 8;
#else // FEATURE_HUD_IMPROVED
DWORD scaleH = GetTextScaleH(textInfo->scaleH);
bgndOffX = (req->pixWidth * scaleH / PHD_ONE) / 2 - T_GetTextWidth(textInfo) / 2 - (8 * scaleH / PHD_ONE);
#endif // FEATURE_HUD_IMPROVED
textInfo->xPos = req->xPos - bgndOffX;
textInfo->bgndOffX = bgndOffX;
}
void __cdecl ReqItemRightAlign(REQUEST_INFO *req, TEXT_STR_INFO *textInfo) {
int bgndOffX;
if( textInfo == NULL )
return;
#ifdef FEATURE_HUD_IMPROVED
bgndOffX = (req->pixWidth - T_GetTextWidth(textInfo)) / 2 - 8;
#else // FEATURE_HUD_IMPROVED
DWORD scaleH = GetTextScaleH(textInfo->scaleH);
bgndOffX = (req->pixWidth * scaleH / PHD_ONE) / 2 - T_GetTextWidth(textInfo) / 2 - (8 * scaleH / PHD_ONE);
#endif // FEATURE_HUD_IMPROVED
textInfo->xPos = req->xPos + bgndOffX;
textInfo->bgndOffX = -bgndOffX;
}
int __cdecl Display_Requester(REQUEST_INFO *req, BOOL removeOnDeselect, BOOL isBackground) {
int i, linesCount, boxHeight, boxOff, linesOff;
DWORD renderWidth, renderHeight;
linesCount = req->visibleCount;
#ifdef FEATURE_HUD_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode ) {
boxHeight = req->lineHeight * linesCount + 42;
boxOff = req->yPos - boxHeight + 2;
linesOff = boxOff + 30;
} else {
boxHeight = req->lineHeight * (linesCount + 1) + 22;
boxOff = req->yPos - boxHeight + 2;
linesOff = boxOff + req->lineHeight + 10;
}
renderWidth = GetRenderWidthDownscaled();
renderHeight = GetRenderHeightDownscaled();
#else // !FEATURE_HUD_IMPROVED
boxHeight = req->lineHeight * (linesCount + 1) + 22;
boxOff = req->yPos - boxHeight + 2;
linesOff = boxOff + req->lineHeight + 10;
renderWidth = GetRenderWidth();
renderHeight = GetRenderHeight();
#endif // FEATURE_HUD_IMPROVED
if( renderWidth != req->renderWidth || renderHeight != req->renderHeight ) {
Remove_Requester(req);
req->renderWidth = renderWidth;
req->renderHeight = renderHeight;
}
req->lpItemFlags1 = RequesterItemFlags1;
req->lpItemFlags2 = RequesterItemFlags2;
if( req->itemsCount < req->visibleCount ) {
linesCount = req->itemsCount;
}
// Heading 1
if( CHK_ANY(req->headingFlags1, REQFLAG_ACTIVE) ) {
if( req->headingText1 == NULL ) {
req->headingText1 = T_Print(req->xPos, boxOff, req->zPos, req->headingString1);
T_CentreH(req->headingText1, 1);
T_BottomAlign(req->headingText1, 1);
if( isBackground ) {
T_AddBackground(req->headingText1, (req->pixWidth - 4), 0, 0, 0, REQ_NEARZ, ICLR_Black, &ReqMainGour1, 2);
T_AddOutline(req->headingText1, TRUE, ICLR_Orange, &ReqMainGour2, 0);
}
}
if( CHK_ANY(req->headingFlags1, REQFLAG_LEFT) ) {
ReqItemLeftAlign(req, req->headingText1);
}
if( CHK_ANY(req->headingFlags1, REQFLAG_RIGHT) ) {
ReqItemRightAlign(req, req->headingText1);
}
}
// Heading 2
if( CHK_ANY(req->headingFlags2, REQFLAG_ACTIVE) ) {
if( req->headingText2 == NULL ) {
req->headingText2 = T_Print(req->xPos, boxOff, req->zPos, req->headingString2);
T_CentreH(req->headingText2, 1);
T_BottomAlign(req->headingText2, 1);
if( isBackground ) {
T_AddBackground(req->headingText2, (req->pixWidth - 4), 0, 0, 0, REQ_NEARZ, ICLR_Black, &ReqMainGour1, 2);
T_AddOutline(req->headingText2, TRUE, ICLR_Orange, &ReqMainGour2, 0);
}
}
if( CHK_ANY(req->headingFlags2, REQFLAG_LEFT) ) {
ReqItemLeftAlign(req, req->headingText2);
}
if( CHK_ANY(req->headingFlags2, REQFLAG_RIGHT) ) {
ReqItemRightAlign(req, req->headingText2);
}
}
// Background
if( isBackground && req->backgroundText == NULL && CHK_ANY(req->backgroundFlags, REQFLAG_ACTIVE) ) {
req->backgroundText = T_Print(req->xPos, boxOff - 2, 0, " ");
T_CentreH(req->backgroundText, 1);
T_BottomAlign(req->backgroundText, 1);
T_AddBackground(req->backgroundText, req->pixWidth, boxHeight, 0, 0, REQ_FARZ, ICLR_Black, &ReqBgndGour1, 1);
T_AddOutline(req->backgroundText, TRUE, ICLR_Blue, &ReqBgndGour2, 0);
}
// More up
if( req->lineOffset == 0 ) {
T_RemovePrint(req->moreupText);
req->moreupText = NULL;
}
else if( req->moreupText == NULL && CHK_ANY(req->moreupFlags, REQFLAG_ACTIVE) ) {
#ifdef FEATURE_HUD_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode ) {
req->moreupText = T_Print(req->xPos, boxOff + 15, 0, MoreUpString);
}
#endif // FEATURE_HUD_IMPROVED
T_CentreH(req->moreupText, 1);
T_BottomAlign(req->moreupText, 1);
}
// More down
if( req->itemsCount <= (req->visibleCount + req->lineOffset) ) {
T_RemovePrint(req->moredownText);
req->moredownText = 0;
}
else if( req->moredownText == NULL && CHK_ANY(req->moredownFlags, REQFLAG_ACTIVE) ) {
#ifdef FEATURE_HUD_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode ) {
req->moredownText = T_Print(req->xPos, req->yPos - 12, 0, MoreDownString);
}
#endif // FEATURE_HUD_IMPROVED
T_CentreH(req->moredownText, 1);
T_BottomAlign(req->moredownText, 1);
}
// Lines init
for( i = 0; i < linesCount; ++i ) {
if( CHK_ANY(req->lpItemFlags1[req->lineOffset + i], REQFLAG_ACTIVE) ) {
if( req->itemTexts1[i] == NULL ) {
// NOTE: here was 0 instead of REQ_NEARZ in the original game
req->itemTexts1[i] = T_Print(0, (linesOff + req->lineHeight * i), REQ_NEARZ, &req->lpItemStrings1[(req->lineOffset + i) * req->itemStringLen]);
T_CentreH(req->itemTexts1[i], 1);
T_BottomAlign(req->itemTexts1[i], 1);
}
if( CHK_ANY(req->reqFlags, REQFLAG_NOCURSOR) || (req->lineOffset + i != req->selected) ) {
req->itemTexts1[i]->zPos = REQ_NEARZ; // NOTE: this line is absent in the original game
T_RemoveBackground(req->itemTexts1[i]);
T_RemoveOutline(req->itemTexts1[i]);
} else {
req->itemTexts1[i]->zPos = 0; // NOTE: this line is absent in the original game
T_AddBackground(req->itemTexts1[i], (req->pixWidth - 12), 0, 0, 0, REQ_MIDZ, ICLR_Black, &ReqSelGour1, 1);
T_AddOutline(req->itemTexts1[i], TRUE, ICLR_Orange, &ReqSelGour2, 0);
}
if( CHK_ANY(req->lpItemFlags1[req->lineOffset + i], REQFLAG_LEFT) ) {
ReqItemLeftAlign(req, req->itemTexts1[i]);
}
else if( CHK_ANY(req->lpItemFlags1[req->lineOffset + i], REQFLAG_RIGHT) ) {
ReqItemRightAlign(req, req->itemTexts1[i]);
}
else {
ReqItemCentreAlign(req, req->itemTexts1[i]);
}
} else {
T_RemovePrint(req->itemTexts1[i]);
T_RemoveBackground(req->itemTexts1[i]);
T_RemoveOutline(req->itemTexts1[i]);
req->itemTexts1[i] = NULL;
}
if( CHK_ANY(req->lpItemFlags2[req->lineOffset + i], REQFLAG_ACTIVE) ) {
if( req->itemTexts2[i] == NULL ) {
// NOTE: here was 0 instead of REQ_NEARZ in the original game
req->itemTexts2[i] = T_Print(0, (linesOff + req->lineHeight * i), REQ_NEARZ, &req->lpItemStrings2[(req->lineOffset + i) * req->itemStringLen]);
T_CentreH(req->itemTexts2[i], 1);
T_BottomAlign(req->itemTexts2[i], 1);
}
// NOTE: this code block is absent in the original game
if( CHK_ANY(req->reqFlags, REQFLAG_NOCURSOR) || (req->lineOffset + i != req->selected) ) {
req->itemTexts2[i]->zPos = REQ_NEARZ;
} else {
req->itemTexts2[i]->zPos = 0;
}
if( CHK_ANY(req->lpItemFlags2[req->lineOffset + i], REQFLAG_LEFT) ) {
ReqItemLeftAlign(req, req->itemTexts2[i]);
}
else if( CHK_ANY(req->lpItemFlags2[req->lineOffset + i], REQFLAG_RIGHT) ) {
ReqItemRightAlign(req, req->itemTexts2[i]);
}
else {
ReqItemCentreAlign(req, req->itemTexts2[i]);
}
} else {
T_RemovePrint(req->itemTexts2[i]);
T_RemoveBackground(req->itemTexts2[i]);
T_RemoveOutline(req->itemTexts2[i]);
req->itemTexts2[i] = NULL;
}
}
// Lines change
if( req->lineOffset != req->lineOldOffset ) {
for( i = 0; i < linesCount; ++i ) {
if( req->itemTexts1[i] != NULL && CHK_ANY(req->lpItemFlags1[req->lineOffset + i], REQFLAG_ACTIVE) ) {
T_ChangeText(req->itemTexts1[i], &req->lpItemStrings1[(req->lineOffset + i) * req->itemStringLen]);
}
if( CHK_ANY(req->lpItemFlags1[req->lineOffset + i], REQFLAG_LEFT) ) {
ReqItemLeftAlign(req, req->itemTexts1[i]);
}
else if( CHK_ANY(req->lpItemFlags1[req->lineOffset + i], REQFLAG_RIGHT) ) {
ReqItemRightAlign(req, req->itemTexts1[i]);
}
else {
ReqItemCentreAlign(req, req->itemTexts1[i]);
}
if( req->itemTexts2[i] != NULL && CHK_ANY(req->lpItemFlags2[req->lineOffset + i], REQFLAG_ACTIVE) ) {
T_ChangeText(req->itemTexts2[i], &req->lpItemStrings2[(req->lineOffset + i) * req->itemStringLen]);
}
if( CHK_ANY(req->lpItemFlags2[req->lineOffset + i], REQFLAG_LEFT) ) {
ReqItemLeftAlign(req, req->itemTexts2[i]);
}
else if( CHK_ANY(req->lpItemFlags2[req->lineOffset + i], REQFLAG_RIGHT) ) {
ReqItemRightAlign(req, req->itemTexts2[i]);
}
else {
ReqItemCentreAlign(req, req->itemTexts2[i]);
}
}
}
// Menu down
if( CHK_ANY(InputDB, IN_BACK) ) {
if( CHK_ANY(req->reqFlags, REQFLAG_NOCURSOR) ) { // Cursor is disabled. Move the list
req->lineOldOffset = req->lineOffset;
if( req->lineOffset < (req->itemsCount - req->visibleCount) ) {
++req->lineOffset;
}
}
else { // Cursor is enabled. Move the cursor
if( req->selected < (req->itemsCount - 1) ) {
++req->selected;
}
req->lineOldOffset = req->lineOffset;
if( req->lineOffset <= (req->selected - req->visibleCount) ) {
++req->lineOffset;
}
}
return 0;
}
// Menu Up
if( CHK_ANY(InputDB, IN_FORWARD) ) {
if( CHK_ANY(req->reqFlags, REQFLAG_NOCURSOR) ) { // Cursor is disabled. Move the list
req->lineOldOffset = req->lineOffset;
if( req->lineOffset > 0 ) {
--req->lineOffset;
}
}
else { // Cursor is enabled. Move the cursor
if( req->selected > 0 ) {
--req->selected;
}
req->lineOldOffset = req->lineOffset;
if( req->lineOffset > req->selected ) {
--req->lineOffset;
}
}
return 0;
}
// Menu Select
if( CHK_ANY(InputDB, IN_SELECT) ) {
// This check prevents from loading an empty saved game slot
// NOTE: there was an ugly strcmp check in the original game
if( req == &LoadGameRequester && InventoryExtraData[0] == 0 && !SaveSlotFlags[req->selected] ) {
InputStatus = 0;
return 0;
} else {
Remove_Requester(req);
return (req->selected + 1);
}
}
// Menu Deselect
if( CHK_ANY(InputDB, IN_DESELECT) && removeOnDeselect ) {
Remove_Requester(req);
return -1;
}
return 0;
}
void __cdecl SetRequesterHeading(REQUEST_INFO *req, const char *string1, DWORD flags1, const char *string2, DWORD flags2) {
T_RemovePrint(req->headingText1);
req->headingText1 = NULL;
T_RemovePrint(req->headingText2);
req->headingText2 = NULL;
if( string1 != NULL ) {
strcpy(req->headingString1, string1);
req->headingFlags1 = flags1 | REQFLAG_ACTIVE;
} else {
strcpy(req->headingString1, "u");
req->headingFlags1 = 0;
}
if( string2 != NULL ) {
strcpy(req->headingString2, string2);
req->headingFlags2 = flags2 | REQFLAG_ACTIVE;
} else {
strcpy(req->headingString2, "u");
req->headingFlags2 = 0;
}
}
void __cdecl RemoveAllReqItems(REQUEST_INFO *req) {
req->itemsCount = 0;
req->lineOffset = 0;
req->selected = 0;
}
void __cdecl ChangeRequesterItem(REQUEST_INFO *req, DWORD itemIdx, const char *string1, DWORD flags1, const char *string2, DWORD flags2) {
T_RemovePrint(req->itemTexts1[itemIdx]);
req->itemTexts1[itemIdx] = NULL;
T_RemovePrint(req->itemTexts2[itemIdx]);
req->itemTexts2[itemIdx] = NULL;
if( string1 != NULL ) {
strcpy(&req->lpItemStrings1[itemIdx * req->itemStringLen], string1);
req->lpItemFlags1[itemIdx] = flags1 | REQFLAG_ACTIVE;
} else {
req->lpItemFlags1[itemIdx] = 0;
}
if( string2 != NULL ) {
strcpy(&req->lpItemStrings2[itemIdx * req->itemStringLen], string2);
req->lpItemFlags2[itemIdx] = flags2 | REQFLAG_ACTIVE;
} else {
req->lpItemFlags2[itemIdx] = 0;
}
}
void __cdecl AddRequesterItem(REQUEST_INFO *req, const char *string1, DWORD flags1, const char *string2, DWORD flags2) {
req->lpItemFlags1 = RequesterItemFlags1;
req->lpItemFlags2 = RequesterItemFlags2;
if( string1 != NULL ) {
strcpy(&req->lpItemStrings1[req->itemsCount * req->itemStringLen], string1);
req->lpItemFlags1[req->itemsCount] = flags1 | REQFLAG_ACTIVE;
} else {
req->lpItemFlags1[req->itemsCount] = 0;
}
if( string2 != NULL ) {
strcpy(&req->lpItemStrings2[req->itemsCount * req->itemStringLen], string2);
req->lpItemFlags2[req->itemsCount] = flags2 | REQFLAG_ACTIVE;
} else {
req->lpItemFlags2[req->itemsCount] = 0;
}
++req->itemsCount;
}
void __cdecl SetPCRequesterSize(REQUEST_INFO *req, int maxLines, __int16 yPos) {
req->yPos = yPos;
#ifdef FEATURE_HUD_IMPROVED
req->visibleCount = GetRenderHeightDownscaled() / 2 / REQ_LN_HEIGHT;
#else // !FEATURE_HUD_IMPROVED
req->visibleCount = GetRenderHeight() / 2 / REQ_LN_HEIGHT;
#endif // FEATURE_HUD_IMPROVED
if( req->visibleCount > maxLines )
req->visibleCount = maxLines;
}
BOOL __cdecl AddAssaultTime(DWORD newTime) {
int i, j;
for( i = 0; i < 10; ++i ) {
if( Assault.bestTime[i] == 0 || newTime < Assault.bestTime[i] ) {
break;
}
}
if( i > 9 ) { // Not best time at all
// NOTE: finishCount is not incremented in the original code in case of bad time
// but in my opinion, bad finish should be counted here, as well as good.
++Assault.finishCount;
return FALSE;
}
// Insertion required if the finish slot is not the last one
for( j = 9; j > i; --j ) {
Assault.bestTime[j] = Assault.bestTime[j - 1];
Assault.bestFinish[j] = Assault.bestFinish[j - 1];
}
Assault.bestTime[i] = newTime;
Assault.bestFinish[i] = ++Assault.finishCount;
#ifdef FEATURE_ASSAULT_SAVE
SaveAssault();
#endif // FEATURE_ASSAULT_SAVE
return TRUE;
}
void __cdecl ShowGymStatsText() {
static bool isStatsTextReady = false;
int minutes, seconds, deciseconds;
char statStr1[32];
char statStr2[32];
if( !isStatsTextReady ) {
StatsRequester.reqFlags |= REQFLAG_NOCURSOR;
SetPCRequesterSize(&StatsRequester, STATS_LN_COUNT, STATS_Y_POS);
StatsRequester.lineHeight = REQ_LN_HEIGHT;
StatsRequester.itemsCount = 0;
StatsRequester.selected = 0;
StatsRequester.lineOffset = 0;
StatsRequester.lineOldOffset = 0;
StatsRequester.pixWidth = STATS_WIDTH;
StatsRequester.xPos = 0;
StatsRequester.zPos = 0;
StatsRequester.lpItemStrings1 = (char *)SaveGameStrings1;
StatsRequester.lpItemStrings2 = (char *)SaveGameStrings2;
StatsRequester.itemStringLen = 50;
Init_Requester(&StatsRequester);
SetRequesterHeading(&StatsRequester, GF_GameStringTable[GSI_String_BestTimes], 0, NULL, 0);
for( int i = 0; i < 10; ++i ) {
if( Assault.bestTime[i] == 0 ) {
if( i == 0 ) {
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_NoTimesSet], 0, NULL, 0);
}
break;
}
deciseconds = Assault.bestTime[i] % 30 / 3;
seconds = Assault.bestTime[i] / 30 % 60;
minutes = Assault.bestTime[i] / 30 / 60;
sprintf(statStr1, "%2d: %s %d", (i + 1), GF_GameStringTable[GSI_String_Finish], (int)Assault.bestFinish[i]);
sprintf(statStr2, "%02d:%02d.%-2d", minutes, seconds, deciseconds);
AddRequesterItem(&StatsRequester, statStr1, REQFLAG_LEFT, statStr2, REQFLAG_RIGHT);
}
isStatsTextReady = true;
}
else if( Display_Requester(&StatsRequester, TRUE, TRUE) ) {
isStatsTextReady = false;
}
else {
InputDB = 0;
InputStatus = 0;
}
}
void __cdecl ShowStatsText(char *timeString, BOOL removeOnDeselect) {
static bool isStatsTextReady = false;
bool isSecret1, isSecret2, isSecret3;
int bufLen, distance;
char bufStr[32];
if( !isStatsTextReady ) {
StatsRequester.reqFlags |= REQFLAG_NOCURSOR;
SetPCRequesterSize(&StatsRequester, STATS_LN_COUNT, STATS_Y_POS);
StatsRequester.lineHeight = REQ_LN_HEIGHT;
StatsRequester.itemsCount = 0;
StatsRequester.selected = 0;
StatsRequester.lineOffset = 0;
StatsRequester.lineOldOffset = 0;
StatsRequester.pixWidth = STATS_WIDTH;
StatsRequester.xPos = 0;
StatsRequester.zPos = 0;
StatsRequester.lpItemStrings1 = (char *)SaveGameStrings1;
StatsRequester.lpItemStrings2 = (char *)SaveGameStrings2;
StatsRequester.itemStringLen = 50;
Init_Requester(&StatsRequester);
SetRequesterHeading(&StatsRequester, GF_LevelNamesStringTable[CurrentLevel], 0, NULL, 0);
// Time taken
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_TimeTaken], REQFLAG_LEFT, timeString, REQFLAG_RIGHT);
// Secrets found
if( GF_NumSecrets > 0 ) {
isSecret1 = CHK_ANY(SaveGame.statistics.secrets, 1);
isSecret2 = CHK_ANY(SaveGame.statistics.secrets, 2);
isSecret3 = CHK_ANY(SaveGame.statistics.secrets, 4);
if( isSecret1 || isSecret2 || isSecret3 ) {
bufLen = 0;
if( isSecret1 ) {
bufStr[bufLen++] = CHAR_SECRET1;
} else {
bufStr[bufLen++] = ' ';
bufStr[bufLen++] = ' ';
bufStr[bufLen++] = ' ';
}
if( isSecret2 ) {
bufStr[bufLen++] = CHAR_SECRET2;
} else {
bufStr[bufLen++] = ' ';
bufStr[bufLen++] = ' ';
bufStr[bufLen++] = ' ';
}
if( isSecret3 ) {
bufStr[bufLen++] = CHAR_SECRET3;
} else {
bufStr[bufLen++] = ' ';
bufStr[bufLen++] = ' ';
bufStr[bufLen++] = ' ';
}
bufStr[bufLen] = 0;
} else {
sprintf(bufStr, GF_GameStringTable[GSI_String_None]);
}
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_SecretsFound], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
}
// Kills
sprintf(bufStr, "%d", (int)SaveGame.statistics.kills);
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_Kills], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
// Ammo used
sprintf(bufStr, "%d", (int)SaveGame.statistics.shots);
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_AmmoUsed], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
// Hits
sprintf(bufStr, "%d", (int)SaveGame.statistics.hits);
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_Hits], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
// HealthPacks used
if( (SaveGame.statistics.mediPacks % 2) == 0 ) {
sprintf(bufStr, "%d.0", (int)(SaveGame.statistics.mediPacks / 2) );
} else {
sprintf(bufStr, "%d.5", (int)(SaveGame.statistics.mediPacks / 2) );
}
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_HealthPacksUsed], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
// Distance travelled
distance = SaveGame.statistics.distance / 445;
if( distance < 1000 ) {
sprintf(bufStr, "%dm", distance);
} else {
sprintf(bufStr, "%d.%02dkm", (distance / 1000), (distance % 100));
}
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_DistanceTravelled], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
isStatsTextReady = true;
} else {
ChangeRequesterItem(&StatsRequester, 0, GF_GameStringTable[GSI_String_TimeTaken], REQFLAG_LEFT, timeString, REQFLAG_RIGHT);
if( Display_Requester(&StatsRequester, removeOnDeselect, TRUE) ) {
isStatsTextReady = false;
} else {
InputDB = 0;
InputStatus = 0;
}
}
}
void __cdecl ShowEndStatsText() {
static bool isStatsTextReady = false;
int i, numLevels;
int total, maxTotal;
int hours, minutes, seconds;
char bufStr[32];
numLevels = GF_GameFlow.num_Levels - GF_GameFlow.num_Demos;
CLAMPG(numLevels, CurrentLevel+1); // NOTE: Fix for Gold. Don't count statistics for bonus levels!
if( !isStatsTextReady ) {
StatsRequester.reqFlags |= REQFLAG_NOCURSOR;
SetPCRequesterSize(&StatsRequester, STATS_LN_COUNT, STATS_Y_POS);
StatsRequester.lineHeight = REQ_LN_HEIGHT;
StatsRequester.itemsCount = 0;
StatsRequester.selected = 0;
StatsRequester.lineOffset = 0;
StatsRequester.lineOldOffset = 0;
StatsRequester.pixWidth = STATS_WIDTH;
StatsRequester.xPos = 0;
StatsRequester.zPos = 0;
StatsRequester.lpItemStrings1 = (char *)SaveGameStrings1;
StatsRequester.lpItemStrings2 = (char *)SaveGameStrings2;
StatsRequester.itemStringLen = 50;
Init_Requester(&StatsRequester);
SetRequesterHeading(&StatsRequester, GF_GameStringTable[GSI_String_FinalStatistics], 0, NULL, 0);
// Time taken
total = 0;
for( i = 1; i < numLevels; ++i ) {
total += SaveGame.start[i].statistics.timer;
}
seconds = total / 30 % 60;
minutes = total / 30 / 60 % 60;
hours = total / 30 / 60 / 60;
sprintf(bufStr, "%02d:%02d:%02d", hours, minutes, seconds);
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_TimeTaken], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
// Secrets found
total = 0;
maxTotal = 0;
// NOTE: In the original code there was hardcode for secrets: for( i = 1; i < (numLevels - 2); ++i )
for( i = 1; i < numLevels; ++i ) {
total += CHK_ANY(SaveGame.start[i].statistics.secrets, 1) ? 1 : 0;
total += CHK_ANY(SaveGame.start[i].statistics.secrets, 2) ? 1 : 0;
total += CHK_ANY(SaveGame.start[i].statistics.secrets, 4) ? 1 : 0;
maxTotal += GF_GetNumSecrets(i); // In the original code there is 3 instead of GF_GetNumSecrets function
}
sprintf(bufStr, "%d %s %d", total, GF_GameStringTable[GSI_String_Of], maxTotal);
#ifdef FEATURE_GOLD
// NOTE: this check is presented in the "Golden Mask" only
if( total == maxTotal ) {
EnableLevelSelect();
}
#endif // FEATURE_GOLD
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_SecretsFound], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
// Kills
total = 0;
for( i = 1; i < numLevels; ++i ) {
total += SaveGame.start[i].statistics.kills;
}
sprintf(bufStr, "%d", total);
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_Kills], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
// Ammo used
total = 0;
for( i = 1; i < numLevels; ++i ) {
total += SaveGame.start[i].statistics.shots;
}
sprintf(bufStr, "%d", total);
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_AmmoUsed], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
// Hits
total = 0;
for( i = 1; i < numLevels; ++i ) {
total += SaveGame.start[i].statistics.hits;
}
sprintf(bufStr, "%d", total);
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_Hits], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
// HealthPacks used
total = 0;
for( i = 1; i < numLevels; ++i ) {
total += SaveGame.start[i].statistics.mediPacks;
}
if( (total % 2) == 0 ) {
sprintf(bufStr, "%d.0", (total / 2));
} else {
sprintf(bufStr, "%d.5", (total / 2));
}
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_HealthPacksUsed], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
// Distance travelled
total = 0;
for( i = 1; i < numLevels; ++i ) {
total += SaveGame.start[i].statistics.distance;
}
total /= 445;
if( total < 1000 )
sprintf(bufStr, "%dm", total);
else
sprintf(bufStr, "%d.%02dkm", total / 1000, total % 100);
AddRequesterItem(&StatsRequester, GF_GameStringTable[GSI_String_DistanceTravelled], REQFLAG_LEFT, bufStr, REQFLAG_RIGHT);
isStatsTextReady = true;
}
else if( Display_Requester(&StatsRequester, FALSE, TRUE) ) {
isStatsTextReady = false;
} else {
InputDB = 0;
InputStatus = 0;
}
}
/*
* Inject function
*/
void Inject_InvText() {
INJECT(0x00425580, Init_Requester);
INJECT(0x00425610, Remove_Requester);
INJECT(0x004256C0, ReqItemCentreAlign);
INJECT(0x004256E0, ReqItemLeftAlign);
INJECT(0x00425740, ReqItemRightAlign);
INJECT(0x004257A0, Display_Requester);
INJECT(0x00426010, SetRequesterHeading);
INJECT(0x004260C0, RemoveAllReqItems);
INJECT(0x004260E0, ChangeRequesterItem);
INJECT(0x004261A0, AddRequesterItem);
INJECT(0x00426250, SetPCRequesterSize);
INJECT(0x00426290, AddAssaultTime);
INJECT(0x00426320, ShowGymStatsText);
INJECT(0x00426500, ShowStatsText);
INJECT(0x004268A0, ShowEndStatsText);
}
================================================
FILE: game/invtext.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef INVTEXT_H_INCLUDED
#define INVTEXT_H_INCLUDED
#include "global/types.h"
// Requester flags
#define REQFLAG_NOCURSOR (1)
// Requester item flags
#define REQFLAG_ACTIVE (1)
#define REQFLAG_LEFT (2)
#define REQFLAG_RIGHT (4)
/*
* Function list
*/
void __cdecl Init_Requester(REQUEST_INFO *req); // 0x00425580
void __cdecl Remove_Requester(REQUEST_INFO *req); // 0x00425610
void __cdecl ReqItemCentreAlign(REQUEST_INFO *req, TEXT_STR_INFO *textInfo); // 0x004256C0
void __cdecl ReqItemLeftAlign(REQUEST_INFO *req, TEXT_STR_INFO *textInfo); // 0x004256E0
void __cdecl ReqItemRightAlign(REQUEST_INFO *req, TEXT_STR_INFO *textInfo); // 0x00425740
int __cdecl Display_Requester(REQUEST_INFO *req, BOOL removeOnDeselect, BOOL isBackground); // 0x004257A0
void __cdecl SetRequesterHeading(REQUEST_INFO *req, const char *string1, DWORD flags1, const char *string2, DWORD flags2); // 0x00426010
void __cdecl RemoveAllReqItems(REQUEST_INFO *req); // 0x004260C0
void __cdecl ChangeRequesterItem(REQUEST_INFO *req, DWORD itemIdx, const char *string1, DWORD flags1, const char *string2, DWORD flags2); // 0x004260E0
void __cdecl AddRequesterItem(REQUEST_INFO *req, const char *string1, DWORD flags1, const char *string2, DWORD flags2); // 0x004261A0
void __cdecl SetPCRequesterSize(REQUEST_INFO *req, int maxLines, __int16 yPos); // 0x00426250
BOOL __cdecl AddAssaultTime(DWORD newTime); // 0x00426290
void __cdecl ShowGymStatsText(); // 0x00426320
void __cdecl ShowStatsText(char *timeString, BOOL removeOnDeselect); // 0x00426500
void __cdecl ShowEndStatsText(); // 0x004268A0
#endif // INVTEXT_H_INCLUDED
================================================
FILE: game/items.cpp
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/items.h"
#include "global/vars.h"
void __cdecl InitialiseItemArray(int itemCount) {
int i;
NextItemFree = LevelItemCount;
PrevItemActive = -1;
NextItemActive = -1;
for( i = LevelItemCount; i+1 < itemCount; ++i ) {
Items[i].active = 0;
Items[i].nextItem = i+1;
}
Items[i].nextItem = -1;
}
void __cdecl InitialiseItem(__int16 itemIndex) {
ITEM_INFO *item;
ROOM_INFO *room;
FLOOR_INFO *floor;
item = &Items[itemIndex];
item->animNumber = Objects[item->objectID].animIndex;
item->frameNumber = Anims[item->animNumber].frameBase;
item->goalAnimState = Anims[item->animNumber].currentAnimState;
item->currentAnimState = item->goalAnimState;
item->requiredAnimState = 0;
item->pos.rotX = 0;
item->pos.rotZ = 0;
item->speed = 0;
item->fallSpeed = 0;
item->hitPoints = Objects[item->objectID].hitPoints;
item->timer = 0;
item->meshBits = 0xFFFFFFFF;
item->touchBits = 0;
item->data = NULL;
item->active = 0;
item->status = ITEM_INACTIVE;
item->gravity = 0;
item->hit_status = 0;
item->collidable = 1;
item->looked_at = 0;
item->clear_body = 0;
if( CHK_ALL(item->flags, IFL_INVISIBLE) ) {
item->status = ITEM_INVISIBLE;
item->flags &= ~IFL_INVISIBLE;
} else if ( Objects[item->objectID].intelligent ) {
item->status = ITEM_INVISIBLE;
}
if( CHK_ALL(item->flags, IFL_CLEARBODY) ) {
item->clear_body = 1;
item->flags &= ~IFL_CLEARBODY;
}
if ( CHK_ALL(item->flags, IFL_CODEBITS) ) {
item->flags &= ~IFL_CODEBITS;
item->flags |= IFL_REVERSE;
AddActiveItem(itemIndex);
item->status = ITEM_ACTIVE;
}
room = &RoomInfo[item->roomNumber];
item->nextItem = room->itemNumber;
room->itemNumber = itemIndex;
floor = &room->floor[((item->pos.z - room->z) >> WALL_SHIFT) + room->xSize * ((item->pos.x - room->x) >> WALL_SHIFT)];
item->floor = floor->floor << 8;
if ( SaveGame.bonusFlag && !IsDemoLevelType )
item->hitPoints *= 2;
if( Objects[item->objectID].initialise != NULL )
Objects[item->objectID].initialise(itemIndex);
}
void __cdecl AddActiveItem(__int16 itemIndex) {
ITEM_INFO *item = &Items[itemIndex];
if( Objects[item->objectID].control == NULL ) {
item->status = ITEM_INACTIVE;
}
else if( item->active == 0 ) {
item->active = 1;
item->nextActive = NextItemActive;
NextItemActive = itemIndex;
}
}
int __cdecl GlobalItemReplace(int oldItemID, int newItemID) {
int i, j;
int result = 0;
for( i = 0; i < RoomCount; ++i ) {
for( j = RoomInfo[i].itemNumber; j != -1; j = Items[j].nextItem ) {
if( Items[j].objectID == oldItemID ) {
Items[j].objectID = newItemID;
++result;
}
}
}
return result;
}
/*
* Inject function
*/
void Inject_Items() {
INJECT(0x00426CD0, InitialiseItemArray);
// INJECT(0x00426D30, KillItem);
// INJECT(0x00426E50, CreateItem);
INJECT(0x00426E90, InitialiseItem);
// INJECT(0x00427050, RemoveActiveItem);
// INJECT(0x004270E0, RemoveDrawnItem);
INJECT(0x00427150, AddActiveItem);
// INJECT(0x004271B0, ItemNewRoom);
INJECT(0x00427250, GlobalItemReplace);
// INJECT(0x004272D0, InitialiseFXArray);
// INJECT(0x00427300, CreateEffect);
// INJECT(0x00427370, KillEffect);
// INJECT(0x00427460, EffectNewRoom);
// INJECT(0x00427500, ClearBodyBag);
}
================================================
FILE: game/items.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef ITEMS_H_INCLUDED
#define ITEMS_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl InitialiseItemArray(int itemCount); // 0x00426CD0
#define KillItem ((void(__cdecl*)(__int16)) 0x00426D30)
#define CreateItem ((__int16(__cdecl*)(void)) 0x00426E50)
void __cdecl InitialiseItem(__int16 itemIndex); // 0x00426E90
#define RemoveActiveItem ((void(__cdecl*)(__int16)) 0x00427050)
#define RemoveDrawnItem ((void(__cdecl*)(__int16)) 0x004270E0)
void __cdecl AddActiveItem(__int16 itemIndex); // 0x00427150
#define ItemNewRoom ((void(__cdecl*)(__int16, __int16)) 0x004271B0)
int __cdecl GlobalItemReplace(int oldItemID, int newItemID); // 0x00427250
#define InitialiseFXArray ((void(__cdecl*)(void)) 0x004272D0)
#define CreateEffect ((__int16(__cdecl*)(__int16)) 0x00427300)
#define KillEffect ((void(__cdecl*)(__int16)) 0x00427370)
#define EffectNewRoom ((void(__cdecl*)(__int16, __int16)) 0x00427460)
#define ClearBodyBag ((void(__cdecl*)(void)) 0x00427500)
#endif // ITEMS_H_INCLUDED
================================================
FILE: game/lara.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/lara.h"
#include "global/vars.h"
#ifdef FEATURE_GAMEPLAY_FIXES
bool IsLowCeilingJumpFix = true;
#endif // FEATURE_GAMEPLAY_FIXES
void __cdecl lara_col_jumper(ITEM_INFO *item, COLL_INFO *coll) {
coll->badPos = 0x7F00;
coll->badNeg = -0x0180;
coll->badCeiling = 0x00C0;
GetLaraCollisionInfo(item, coll);
LaraDeflectEdgeJump(item, coll);
if( item->fallSpeed > 0 && coll->sideMid.floor <= 0 ) {
item->goalAnimState = LaraLandedBad(item, coll) ? AS_DEATH : AS_STOP;
item->fallSpeed = 0;
item->gravity = 0;
item->pos.y += coll->sideMid.floor;
}
// NOTE: Low ceiling check must be skipped because it produces the bug
// Core Design removed this check in later game releases
#ifdef FEATURE_GAMEPLAY_FIXES
if( IsLowCeilingJumpFix ) return;
#endif // FEATURE_GAMEPLAY_FIXES
if( ABS(coll->sideMid.ceiling - coll->sideMid.floor) < 0x02FA ) {
item->currentAnimState = AS_FASTFALL;
item->goalAnimState = AS_FASTFALL;
item->animNumber = 32;
item->frameNumber = Anims[item->animNumber].frameBase + 1;
item->speed /= 4;
Lara.move_angle += PHD_180;
if( item->fallSpeed <= 0 )
item->fallSpeed = 1;
}
}
/*
* Inject function
*/
void Inject_Lara() {
// INJECT(0x00427560, LaraAboveWater);
// INJECT(0x00427700, LookUpDown);
// INJECT(0x00427770, LookLeftRight);
// INJECT(0x004277F0, ResetLook);
// INJECT(0x00427880, lara_as_walk);
// INJECT(0x00427910, lara_as_run);
// INJECT(0x00427A60, lara_as_stop);
// INJECT(0x00427BB0, lara_as_forwardjump);
// INJECT(----------, lara_as_pose);
// INJECT(0x00427C90, lara_as_fastback);
// INJECT(0x00427CF0, lara_as_turn_r);
// INJECT(0x00427D80, lara_as_turn_l);
// INJECT(0x00427E10, lara_as_death);
// INJECT(0x00427E30, lara_as_fastfall);
// INJECT(0x00427E70, lara_as_hang);
// INJECT(0x00427ED0, lara_as_reach);
// INJECT(0x00427EF0, lara_as_splat);
// INJECT(----------, lara_as_land);
// INJECT(0x00427F00, lara_as_compress);
// INJECT(0x00428010, lara_as_back);
// INJECT(0x004280A0, lara_as_null);
// INJECT(0x004280B0, lara_as_fastturn);
// INJECT(0x00428100, lara_as_stepright);
// INJECT(0x00428180, lara_as_stepleft);
// INJECT(0x00428200, lara_as_slide);
// INJECT(0x00428230, lara_as_backjump);
// INJECT(0x00428280, lara_as_rightjump);
// INJECT(0x004282C0, lara_as_leftjump);
// INJECT(0x00428300, lara_as_upjump);
// INJECT(0x00428320, lara_as_fallback);
// INJECT(0x00428350, lara_as_hangleft);
// INJECT(0x00428390, lara_as_hangright);
// INJECT(0x004283D0, lara_as_slideback);
// INJECT(0x004283F0, lara_as_pushblock);
// INJECT(----------, lara_as_pullblock);
// INJECT(0x00428420, lara_as_ppready);
// INJECT(0x00428450, lara_as_pickup);
// INJECT(0x00428480, lara_as_pickupflare);
// INJECT(0x004284E0, lara_as_switchon);
// INJECT(----------, lara_as_switchoff);
// INJECT(0x00428520, lara_as_usekey);
// INJECT(----------, lara_as_usepuzzle);
// INJECT(----------, lara_as_roll);
// INJECT(----------, lara_as_roll2);
// INJECT(0x00428550, lara_as_special);
// INJECT(----------, lara_as_usemidas);
// INJECT(----------, lara_as_diemidas);
// INJECT(0x00428570, lara_as_swandive);
// INJECT(0x004285A0, lara_as_fastdive);
// INJECT(----------, lara_as_gymnast);
// INJECT(0x00428600, lara_as_waterout);
// INJECT(----------, lara_as_laratest1);
// INJECT(----------, lara_as_laratest2);
// INJECT(----------, lara_as_laratest3);
// INJECT(0x00428620, lara_as_wade);
// INJECT(----------, lara_as_twist);
// INJECT(----------, lara_as_kick);
// INJECT(0x004286F0, lara_as_deathslide);
// INJECT(0x00428790, extra_as_breath);
// INJECT(----------, extra_as_plunger);
// INJECT(0x004287E0, extra_as_yetikill);
// INJECT(0x00428830, extra_as_sharkkill);
// INJECT(0x004288D0, extra_as_airlock);
// INJECT(0x004288F0, extra_as_gongbong);
// INJECT(0x00428910, extra_as_dinokill);
// INJECT(0x00428970, extra_as_pulldagger);
// INJECT(0x00428A30, extra_as_startanim);
// INJECT(0x00428A80, extra_as_starthouse);
// INJECT(0x00428B30, extra_as_finalanim);
// INJECT(0x00428BE0, LaraFallen);
// INJECT(0x00428C40, LaraCollideStop);
// INJECT(0x00428D00, lara_col_walk);
// INJECT(0x00428EA0, lara_col_run);
// INJECT(0x00429020, lara_col_stop);
// INJECT(0x004290B0, lara_col_forwardjump);
// INJECT(----------, lara_col_pose);
// INJECT(0x00429190, lara_col_fastback);
// INJECT(0x00429250, lara_col_turn_r);
// INJECT(0x004292F0, lara_col_turn_l);
// INJECT(0x00429310, lara_col_death);
// INJECT(0x00429380, lara_col_fastfall);
// INJECT(0x00429420, lara_col_hang);
// INJECT(0x00429550, lara_col_reach);
// INJECT(0x004295E0, lara_col_splat);
// INJECT(----------, lara_col_land);
// INJECT(0x00429640, lara_col_compress);
// INJECT(0x004296E0, lara_col_back);
// INJECT(----------, lara_col_null);
// INJECT(0x004297E0, lara_col_fastturn);
// INJECT(0x00429800, lara_col_stepright);
// INJECT(0x004298C0, lara_col_stepleft);
// INJECT(0x004298E0, lara_col_slide);
// INJECT(0x00429900, lara_col_backjump);
// INJECT(0x00429930, lara_col_rightjump);
// INJECT(0x00429960, lara_col_leftjump);
// INJECT(0x00429990, lara_col_upjump);
// INJECT(0x00429AD0, lara_col_fallback);
// INJECT(0x00429B60, lara_col_hangleft);
// INJECT(0x00429BA0, lara_col_hangright);
// INJECT(0x00429BE0, lara_col_slideback);
// INJECT(----------, lara_col_pushblock);
// INJECT(----------, lara_col_pullblock);
// INJECT(----------, lara_col_ppready);
// INJECT(----------, lara_col_pickup);
// INJECT(----------, lara_col_switchon);
// INJECT(----------, lara_col_switchoff);
// INJECT(----------, lara_col_usekey);
// INJECT(----------, lara_col_usepuzzle);
// INJECT(0x00429C10, lara_col_roll);
// INJECT(0x00429CB0, lara_col_roll2);
// INJECT(0x00429D80, lara_col_special);
// INJECT(----------, lara_col_usemidas);
// INJECT(----------, lara_col_diemidas);
// INJECT(0x00429DA0, lara_col_swandive);
// INJECT(0x00429E10, lara_col_fastdive);
// INJECT(----------, lara_col_gymnast);
// INJECT(----------, lara_col_waterout);
// INJECT(----------, lara_col_laratest1);
// INJECT(----------, lara_col_laratest2);
// INJECT(----------, lara_col_laratest3);
// INJECT(0x00429E90, lara_col_wade);
// INJECT(----------, lara_col_twist);
// INJECT(0x0042A000, lara_default_col);
INJECT(0x0042A040, lara_col_jumper);
// INJECT(0x0042A120, lara_col_kick);
// INJECT(----------, lara_col_deathslide);
// INJECT(0x0042A130, GetLaraCollisionInfo);
// INJECT(0x0042A170, lara_slide_slope);
// INJECT(0x0042A260, LaraHitCeiling);
// INJECT(0x0042A2D0, LaraDeflectEdge);
// INJECT(0x0042A350, LaraDeflectEdgeJump);
// INJECT(0x0042A4D0, LaraSlideEdgeJump);
// INJECT(0x0042A5C0, TestWall);
// INJECT(0x0042A6D0, LaraTestHangOnClimbWall);
// INJECT(0x0042A7E0, LaraTestClimbStance);
// INJECT(0x0042A8A0, LaraHangTest);
// INJECT(0x0042AC00, LaraTestEdgeCatch);
// INJECT(0x0042ACB0, LaraTestHangJumpUp);
// INJECT(0x0042AE20, LaraTestHangJump);
// INJECT(0x0042AFC0, TestHangSwingIn);
// INJECT(0x0042B080, TestLaraVault);
// INJECT(0x0042B370, TestLaraSlide);
// INJECT(0x0042B4A0, LaraFloorFront);
// INJECT(0x0042B520, LaraLandedBad);
// INJECT(0x0042B5E0, GetLaraJointAbsPosition);
// INJECT(0x0042B970, GetLJAInt);
}
================================================
FILE: game/lara.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef LARA_H_INCLUDED
#define LARA_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define LaraAboveWater ((void(__cdecl*)(ITEM_INFO*, COLL_INFO*)) 0x00427560)
// 0x00427700: LookUpDown
// 0x00427770: LookLeftRight
// 0x004277F0: ResetLook
// 0x00427880: lara_as_walk
// 0x00427910: lara_as_run
// 0x00427A60: lara_as_stop
// 0x00427BB0: lara_as_forwardjump
// ----------: lara_as_pose
// 0x00427C90: lara_as_fastback
// 0x00427CF0: lara_as_turn_r
// 0x00427D80: lara_as_turn_l
// 0x00427E10: lara_as_death
// 0x00427E30: lara_as_fastfall
// 0x00427E70: lara_as_hang
// 0x00427ED0: lara_as_reach
// 0x00427EF0: lara_as_splat
// ----------: lara_as_land
// 0x00427F00: lara_as_compress
// 0x00428010: lara_as_back
// 0x004280A0: lara_as_null
// 0x004280B0: lara_as_fastturn
// 0x00428100: lara_as_stepright
// 0x00428180: lara_as_stepleft
// 0x00428200: lara_as_slide
// 0x00428230: lara_as_backjump
// 0x00428280: lara_as_rightjump
// 0x004282C0: lara_as_leftjump
// 0x00428300: lara_as_upjump
// 0x00428320: lara_as_fallback
// 0x00428350: lara_as_hangleft
// 0x00428390: lara_as_hangright
// 0x004283D0: lara_as_slideback
// 0x004283F0: lara_as_pushblock
// ----------: lara_as_pullblock
// 0x00428420: lara_as_ppready
// 0x00428450: lara_as_pickup
// 0x00428480: lara_as_pickupflare
// 0x004284E0: lara_as_switchon
// ----------: lara_as_switchoff
// 0x00428520: lara_as_usekey
// ----------: lara_as_usepuzzle
// ----------: lara_as_roll
// ----------: lara_as_roll2
// 0x00428550: lara_as_special
// ----------: lara_as_usemidas
// ----------: lara_as_diemidas
// 0x00428570: lara_as_swandive
// 0x004285A0: lara_as_fastdive
// ----------: lara_as_gymnast
// 0x00428600: lara_as_waterout
// ----------: lara_as_laratest1
// ----------: lara_as_laratest2
// ----------: lara_as_laratest3
// 0x00428620: lara_as_wade
// ----------: lara_as_twist
// ----------: lara_as_kick
// 0x004286F0: lara_as_deathslide
// 0x00428790: extra_as_breath
// ----------: extra_as_plunger
// 0x004287E0: extra_as_yetikill
// 0x00428830: extra_as_sharkkill
// 0x004288D0: extra_as_airlock
// 0x004288F0: extra_as_gongbong
// 0x00428910: extra_as_dinokill
// 0x00428970: extra_as_pulldagger
// 0x00428A30: extra_as_startanim
// 0x00428A80: extra_as_starthouse
// 0x00428B30: extra_as_finalanim
// 0x00428BE0: LaraFallen
// 0x00428C40: LaraCollideStop
// 0x00428D00: lara_col_walk
// 0x00428EA0: lara_col_run
// 0x00429020: lara_col_stop
// 0x004290B0: lara_col_forwardjump
// ----------: lara_col_pose
// 0x00429190: lara_col_fastback
// 0x00429250: lara_col_turn_r
// 0x004292F0: lara_col_turn_l
// 0x00429310: lara_col_death
// 0x00429380: lara_col_fastfall
// 0x00429420: lara_col_hang
// 0x00429550: lara_col_reach
// 0x004295E0: lara_col_splat
// ----------: lara_col_land
// 0x00429640: lara_col_compress
// 0x004296E0: lara_col_back
// ----------: lara_col_null
// 0x004297E0: lara_col_fastturn
// 0x00429800: lara_col_stepright
// 0x004298C0: lara_col_stepleft
// 0x004298E0: lara_col_slide
// 0x00429900: lara_col_backjump
// 0x00429930: lara_col_rightjump
// 0x00429960: lara_col_leftjump
// 0x00429990: lara_col_upjump
// 0x00429AD0: lara_col_fallback
// 0x00429B60: lara_col_hangleft
// 0x00429BA0: lara_col_hangright
// 0x00429BE0: lara_col_slideback
// ----------: lara_col_pushblock
// ----------: lara_col_pullblock
// ----------: lara_col_ppready
// ----------: lara_col_pickup
// ----------: lara_col_switchon
// ----------: lara_col_switchoff
// ----------: lara_col_usekey
// ----------: lara_col_usepuzzle
// 0x00429C10: lara_col_roll
// 0x00429CB0: lara_col_roll2
// 0x00429D80: lara_col_special
// ----------: lara_col_usemidas
// ----------: lara_col_diemidas
// 0x00429DA0: lara_col_swandive
// 0x00429E10: lara_col_fastdive
// ----------: lara_col_gymnast
// ----------: lara_col_waterout
// ----------: lara_col_laratest1
// ----------: lara_col_laratest2
// ----------: lara_col_laratest3
// 0x00429E90: lara_col_wade
// ----------: lara_col_twist
// 0x0042A000: lara_default_col
void __cdecl lara_col_jumper(ITEM_INFO *item, COLL_INFO *coll); // 0x0042A040
// 0x0042A120: lara_col_kick
// ----------: lara_col_deathslide
#define GetLaraCollisionInfo ((void (__cdecl*)(ITEM_INFO *item, COLL_INFO *coll)) 0x0042A130)
// 0x0042A170: lara_slide_slope
// 0x0042A260: LaraHitCeiling
// 0x0042A2D0: LaraDeflectEdge
#define LaraDeflectEdgeJump ((void (__cdecl*)(ITEM_INFO *item, COLL_INFO *coll)) 0x0042A350)
// 0x0042A4D0: LaraSlideEdgeJump
// 0x0042A5C0: TestWall
// 0x0042A6D0: LaraTestHangOnClimbWall
// 0x0042A7E0: LaraTestClimbStance
// 0x0042A8A0: LaraHangTest
// 0x0042AC00: LaraTestEdgeCatch
// 0x0042ACB0: LaraTestHangJumpUp
// 0x0042AE20: LaraTestHangJump
// 0x0042AFC0: TestHangSwingIn
// 0x0042B080: TestLaraVault
// 0x0042B370: TestLaraSlide
// 0x0042B4A0: LaraFloorFront
#define LaraLandedBad ((bool (__cdecl*)(ITEM_INFO *item, COLL_INFO *coll)) 0x0042B520)
#define GetLaraJointAbsPosition ((void(__cdecl*)(PHD_VECTOR*,int)) 0x0042B5E0)
// 0x0042B970: GetLJAInt
#endif // LARA_H_INCLUDED
================================================
FILE: game/lara1gun.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Copyright (c) 2020 ChocolateFan
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/lara1gun.h"
#include "3dsystem/phd_math.h"
#include "game/box.h"
#include "game/control.h"
#include "game/draw.h"
#include "game/items.h"
#include "game/lara.h"
#include "game/larafire.h"
#include "game/objects.h"
#include "game/sound.h"
#include "specific/game.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/joy_output.h"
#endif // FEATURE_INPUT_IMPROVED
#ifdef FEATURE_GAMEPLAY_FIXES
bool IsRunningM16fix = true;
#endif // FEATURE_GAMEPLAY_FIXES
void __cdecl RifleHandler(int weaponType) {
WEAPON_INFO *weapon = &Weapons[weaponType];
if( CHK_ANY(InputStatus, IN_ACTION) )
LaraTargetInfo(&Weapons[weaponType]);
else {
Lara.target = 0;
}
if( !Lara.target ) {
LaraGetNewTarget(weapon);
}
AimWeapon(weapon, &Lara.left_arm);
if( Lara.left_arm.lock ) {
Lara.torso_x_rot = Lara.left_arm.x_rot;
Lara.torso_y_rot = Lara.left_arm.y_rot;
Lara.head_x_rot = Lara.head_y_rot = 0;
}
AnimateShotgun(weaponType);
if( Lara.right_arm.flash_gun && (weaponType == LGT_Shotgun || weaponType == LGT_M16) ) {
int x = LaraItem->pos.x + (phd_sin(LaraItem->pos.rotY) >> (W2V_SHIFT-10));
int y = LaraItem->pos.y - 0x200;
int z = LaraItem->pos.z + (phd_cos(LaraItem->pos.rotY) >> (W2V_SHIFT-10));
AddDynamicLight(x, y, z, 12, 11);
}
}
void __cdecl FireShotgun() {
__int16 base[2], angles[2];
BOOL isFired = FALSE;
base[0] = Lara.left_arm.y_rot + LaraItem->pos.rotY;
base[1] = Lara.left_arm.x_rot;
for( int i=0; i<6; ++i ) {
angles[0] = base[0] + 20*PHD_DEGREE * (GetRandomControl() - PHD_ONE/4) / PHD_ONE;
angles[1] = base[1] + 20*PHD_DEGREE * (GetRandomControl() - PHD_ONE/4) / PHD_ONE;
if( FireWeapon(LGT_Shotgun, Lara.target, LaraItem, angles) ) {
isFired = TRUE;
}
}
if( isFired ) {
Lara.right_arm.flash_gun = Weapons[LGT_Shotgun].flashTime;
PlaySoundEffect(Weapons[LGT_Shotgun].sampleNum, &LaraItem->pos, 0);
#ifdef FEATURE_INPUT_IMPROVED
JoyVibrate(0x2000, 0x2000, 2, 0x800, 5, false);
#endif // FEATURE_INPUT_IMPROVED
}
}
void __cdecl FireM16(BOOL isRunning) {
__int16 angles[2];
angles[0] = Lara.left_arm.y_rot + LaraItem->pos.rotY;
angles[1] = Lara.left_arm.x_rot;
// NOTE: Ther was a bug in the original game - ID_LARA_M16 instead of LGT_M16
#ifdef FEATURE_GAMEPLAY_FIXES
if( IsRunningM16fix && isRunning ) {
Weapons[LGT_M16].shotAccuracy = 12*PHD_DEGREE;
Weapons[LGT_M16].damage = 1;
} else {
Weapons[LGT_M16].shotAccuracy = 4*PHD_DEGREE;
Weapons[LGT_M16].damage = 3;
}
#endif // FEATURE_GAMEPLAY_FIXES
if( FireWeapon(LGT_M16, Lara.target, LaraItem, angles) ) {
Lara.right_arm.flash_gun = Weapons[LGT_M16].flashTime;
#ifdef FEATURE_INPUT_IMPROVED
JoyVibrate(0x400, 0x400, 2, 0x80, 4, false);
#endif // FEATURE_INPUT_IMPROVED
}
}
void __cdecl FireHarpoon() {
GAME_VECTOR pos;
if( Lara.harpoon_ammo <= 0 ) return;
__int16 itemID = CreateItem();
if( itemID < 0 ) return;
ITEM_INFO *item = &Items[itemID];
item->objectID = ID_HARPOON_BOLT;
item->roomNumber = LaraItem->roomNumber;
pos.x = -2;
pos.y = 373;
pos.z = 77;
GetLaraJointAbsPosition((PHD_VECTOR *)&pos, 10);
item->pos.x = pos.x;
item->pos.y = pos.y;
item->pos.z = pos.z;
InitialiseItem(itemID);
if( Lara.target ) {
find_target_point(Lara.target, &pos);
item->pos.rotY = phd_atan(pos.z - item->pos.z, pos.x - item->pos.x);
int distance = phd_sqrt(SQR(pos.x - item->pos.x) + SQR(pos.z - item->pos.z));
item->pos.rotX = -phd_atan(distance, pos.y - item->pos.y);
} else {
item->pos.rotX = Lara.left_arm.x_rot + LaraItem->pos.rotX;
item->pos.rotY = Lara.left_arm.y_rot + LaraItem->pos.rotY;
}
item->pos.rotZ = 0;
item->fallSpeed = -150 * phd_sin(item->pos.rotX) >> W2V_SHIFT;
item->speed = 150 * phd_cos(item->pos.rotX) >> W2V_SHIFT;
AddActiveItem(itemID);
if( !SaveGame.bonusFlag ) {
--Lara.harpoon_ammo;
}
++SaveGame.statistics.shots;
#ifdef FEATURE_INPUT_IMPROVED
JoyVibrate(0xC00, 0xC00, 2, 0x400, 4, false);
#endif // FEATURE_INPUT_IMPROVED
}
void __cdecl FireRocket() {
__int16 itemID;
ITEM_INFO *item;
PHD_VECTOR pos;
if (Lara.grenade_ammo > 0) {
itemID = CreateItem();
if (itemID != -1) {
item = &Items[itemID];
item->objectID = ID_ROCKET;
item->roomNumber = LaraItem->roomNumber;
pos.x = -2;
pos.y = 373;
pos.z = 77;
GetLaraJointAbsPosition(&pos, 10);
item->pos.x = pos.x;
item->pos.y = pos.y;
item->pos.z = pos.z;
InitialiseItem(itemID);
item->pos.rotX = LaraItem->pos.rotX + Lara.left_arm.x_rot;
item->pos.rotZ = 0;
item->speed = 200;
item->fallSpeed = 0;
item->pos.rotY = LaraItem->pos.rotY + Lara.left_arm.y_rot;
AddActiveItem(itemID);
if (!SaveGame.bonusFlag)
--Lara.grenade_ammo;
++SaveGame.statistics.shots;
#ifdef FEATURE_INPUT_IMPROVED
JoyVibrate(0x1000, 0x1000, 2, 0x400, 4, false);
#endif // FEATURE_INPUT_IMPROVED
}
}
}
void __cdecl ControlRocket(__int16 itemID) {
ITEM_INFO *item, *link;
int oldX, oldY, oldZ, displacement, c, s, r, oldR;
__int16 room, linkID, *frame, fxID;
FLOOR_INFO *floor;
BOOL collision;
FX_INFO *fx;
item = &Items[itemID];
oldX = item->pos.x;
oldY = item->pos.y;
oldZ = item->pos.z;
if (item->speed < 190)
++item->fallSpeed;
--item->speed;
item->pos.y += item->fallSpeed - (item->speed * phd_sin(item->pos.rotX) >> W2V_SHIFT);
item->pos.z += phd_cos(item->pos.rotY) * (item->speed * phd_cos(item->pos.rotX) >> W2V_SHIFT) >> W2V_SHIFT;
item->pos.x += phd_sin(item->pos.rotY) * (item->speed * phd_cos(item->pos.rotX) >> W2V_SHIFT) >> W2V_SHIFT;
room = item->roomNumber;
floor = GetFloor(item->pos.x, item->pos.y, item->pos.z, &room);
item->floor = GetHeight(floor, item->pos.x, item->pos.y, item->pos.z);
if (item->roomNumber != room)
ItemNewRoom(itemID, room);
if (item->pos.y < item->floor && item->pos.y > GetCeiling(floor, item->pos.x, item->pos.y, item->pos.z)) {
collision = FALSE;
displacement = 0;
} else {
displacement = 512;
collision = TRUE;
}
for (linkID = RoomInfo[item->roomNumber].itemNumber; linkID != -1; linkID = link->nextItem) {
link = &Items[linkID];
if (link != LaraItem &&
link->collidable &&
(link->objectID == ID_WINDOW1 ||
(Objects[link->objectID].intelligent &&
link->status != ITEM_INVISIBLE &&
Objects[link->objectID].collision)))
{
frame = GetBestFrame(link);
if (item->pos.y + displacement >= link->pos.y + frame[2] && item->pos.y - displacement <= link->pos.y + frame[3]) {
c = phd_cos(link->pos.rotY);
s = phd_sin(link->pos.rotY);
r = (c * (item->pos.x - link->pos.x) - s * (item->pos.z - link->pos.z)) >> W2V_SHIFT;
oldR = (c * (oldX - link->pos.x) - s * (oldZ - link->pos.z)) >> W2V_SHIFT;
if ((r + displacement >= frame[0] ||
oldR + displacement >= frame[0]) &&
(r - displacement <= frame[1] ||
oldR - displacement <= frame[1]))
{
r = (s * (item->pos.x - link->pos.x) + c * (item->pos.z - link->pos.z)) >> W2V_SHIFT;
oldR = (s * (oldX - link->pos.x) + c * (oldZ - link->pos.z)) >> W2V_SHIFT;
if ((r + displacement >= frame[4] ||
oldR + displacement >= frame[4]) &&
(r - displacement <= frame[5] ||
oldR - displacement <= frame[5]))
{
if (link->objectID == ID_WINDOW1) {
SmashWindow(linkID);
} else {
if (link->status == ITEM_ACTIVE) {
HitTarget(link, NULL, 30);
++SaveGame.statistics.hits;
if (link->hitPoints <= 0) {
++SaveGame.statistics.kills;
if (link->objectID != ID_DRAGON_FRONT && link->objectID != ID_GIANT_YETI)
CreatureDie(linkID, TRUE);
}
}
collision = TRUE;
}
}
}
}
}
}
if (collision) {
fxID = CreateEffect(item->roomNumber);
if (fxID != -1) {
fx = &Effects[fxID];
fx->pos.x = oldX;
fx->pos.y = oldY;
fx->pos.z = oldZ;
fx->speed = 0;
fx->frame_number = 0;
fx->counter = 0;
fx->object_number = ID_EXPLOSION;
}
PlaySoundEffect(105, NULL, 0);
KillItem(itemID);
#ifdef FEATURE_INPUT_IMPROVED
JoyRumbleExplode(oldX, oldY, oldZ, 0x1400, true);
#endif // FEATURE_INPUT_IMPROVED
}
}
/*
* Inject function
*/
void Inject_Lara1Gun() {
// INJECT(0x0042BC90, draw_shotgun_meshes);
// INJECT(0x0042BCD0, undraw_shotgun_meshes);
// INJECT(0x0042BD00, ready_shotgun);
INJECT(0x0042BD70, RifleHandler);
INJECT(0x0042BE70, FireShotgun);
INJECT(0x0042BF70, FireM16);
INJECT(0x0042BFF0, FireHarpoon);
// INJECT(0x0042C180, ControlHarpoonBolt);
INJECT(0x0042C4D0, FireRocket);
INJECT(0x0042C5C0, ControlRocket);
// INJECT(0x0042C9D0, draw_shotgun);
// INJECT(0x0042CB40, undraw_shotgun);
// INJECT(0x0042CC50, AnimateShotgun);
}
================================================
FILE: game/lara1gun.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef LARA1GUN_H_INCLUDED
#define LARA1GUN_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x0042BC90: draw_shotgun_meshes
// 0x0042BCD0: undraw_shotgun_meshes
// 0x0042BD00: ready_shotgun
void __cdecl RifleHandler(int weaponType); // 0x0042BD70
void __cdecl FireShotgun(); // 0x0042BE70
void __cdecl FireM16(BOOL isRunning); // 0x0042BF70
void __cdecl FireHarpoon(); // 0x0042BFF0
// 0x0042C180: ControlHarpoonBolt
void __cdecl FireRocket(); // 0x0042C4D0
void __cdecl ControlRocket(__int16 itemID); // 0x0042C5C0
// 0x0042C9D0: draw_shotgun
// 0x0042CB40: undraw_shotgun
#define AnimateShotgun ((void(__cdecl*)(int)) 0x0042CC50)
#endif // LARA1GUN_H_INCLUDED
================================================
FILE: game/lara2gun.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/lara2gun.h"
#include "3dsystem/phd_math.h"
#include "game/draw.h"
#include "game/larafire.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/joy_output.h"
#endif // FEATURE_INPUT_IMPROVED
void __cdecl PistolHandler(int weaponType) {
WEAPON_INFO *weapon = &Weapons[weaponType];
if( CHK_ANY(InputStatus, IN_ACTION) ) {
LaraTargetInfo(weapon);
} else {
Lara.target = 0;
}
if( !Lara.target ) {
LaraGetNewTarget(weapon);
}
AimWeapon(weapon, &Lara.left_arm);
AimWeapon(weapon, &Lara.right_arm);
if( Lara.left_arm.lock && !Lara.right_arm.lock ) {
Lara.head_y_rot = Lara.torso_y_rot = Lara.left_arm.y_rot / 2;
Lara.head_x_rot = Lara.torso_x_rot = Lara.left_arm.x_rot / 2;
} else if( !Lara.left_arm.lock && Lara.right_arm.lock ) {
Lara.head_y_rot = Lara.torso_y_rot = Lara.right_arm.y_rot / 2;
Lara.head_x_rot = Lara.torso_x_rot = Lara.right_arm.x_rot / 2;
} else if ( Lara.left_arm.lock && Lara.right_arm.lock ) {
Lara.head_y_rot = Lara.torso_y_rot = (Lara.right_arm.y_rot + Lara.left_arm.y_rot) / 4;
Lara.head_x_rot = Lara.torso_x_rot = (Lara.right_arm.x_rot + Lara.left_arm.x_rot) / 4;
}
AnimatePistols(weaponType);
if( Lara.left_arm.flash_gun || Lara.right_arm.flash_gun ) {
int x = LaraItem->pos.x + (phd_sin(LaraItem->pos.rotY) >> (W2V_SHIFT-10));
int y = LaraItem->pos.y - 0x200;
int z = LaraItem->pos.z + (phd_cos(LaraItem->pos.rotY) >> (W2V_SHIFT-10));
AddDynamicLight(x, y, z, 12, 11);
#ifdef FEATURE_INPUT_IMPROVED
if( weaponType == LGT_Uzis ) {
JoyVibrate(0x400, 0x400, 1, 0x100, 2, false);
} else if( Lara.left_arm.flash_gun && Lara.right_arm.flash_gun ) {
JoyVibrate(0x600, 0x600, 1, 0x300, 2, false);
} else {
JoyVibrate(0x400, 0x400, 1, 0x100, 2, false);
}
#endif // FEATURE_INPUT_IMPROVED
}
}
/*
* Inject function
*/
void Inject_Lara2Gun() {
// INJECT(0x0042D000, set_pistol_arm);
// INJECT(0x0042D050, draw_pistols);
// INJECT(0x0042D0D0, undraw_pistols);
// INJECT(0x0042D300, ready_pistols);
// INJECT(0x0042D360, draw_pistol_meshes);
// INJECT(0x0042D3B0, undraw_pistol_mesh_left);
// INJECT(0x0042D3F0, undraw_pistol_mesh_right);
INJECT(0x0042D430, PistolHandler);
// INJECT(0x0042D5C0, AnimatePistols);
}
================================================
FILE: game/lara2gun.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef LARA2GUN_H_INCLUDED
#define LARA2GUN_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x0042D000: set_pistol_arm
// 0x0042D050: draw_pistols
// 0x0042D0D0: undraw_pistols
// 0x0042D300: ready_pistols
// 0x0042D360: draw_pistol_meshes
// 0x0042D3B0: undraw_pistol_mesh_left
// 0x0042D3F0: undraw_pistol_mesh_right
void __cdecl PistolHandler(int weaponType); // 0x0042D430
#define AnimatePistols ((void(__cdecl*)(int)) 0x0042D5C0)
#endif // LARA2GUN_H_INCLUDED
================================================
FILE: game/laraclimb.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/laraclimb.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_LaraClimb() {
// INJECT(0x0042D8F0, lara_as_climbleft);
// INJECT(0x0042D930, lara_as_climbright);
// INJECT(0x0042D970, lara_as_climbstnc);
// INJECT(0x0042D9F0, lara_as_climbing);
// INJECT(0x0042DA10, lara_as_climbend);
// INJECT(0x0042DA30, lara_as_climbdown);
// INJECT(0x0042DA50, lara_col_climbleft);
// INJECT(0x0042DAB0, lara_col_climbright);
// INJECT(0x0042DB10, lara_col_climbstnc);
// INJECT(0x0042DD20, lara_col_climbing);
// INJECT(----------, lara_col_climbend);
// INJECT(0x0042DE70, lara_col_climbdown);
// INJECT(0x0042E010, LaraCheckForLetGo);
// INJECT(0x0042E0C0, LaraTestClimb);
// INJECT(0x0042E330, LaraTestClimbPos);
// INJECT(0x0042E400, LaraDoClimbLeftRight);
// INJECT(0x0042E4F0, LaraTestClimbUpPos);
}
================================================
FILE: game/laraclimb.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef LARA_CLIMB_H_INCLUDED
#define LARA_CLIMB_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x0042D8F0: lara_as_climbleft
// 0x0042D930: lara_as_climbright
// 0x0042D970: lara_as_climbstnc
// 0x0042D9F0: lara_as_climbing
// 0x0042DA10: lara_as_climbend
// 0x0042DA30: lara_as_climbdown
// 0x0042DA50: lara_col_climbleft
// 0x0042DAB0: lara_col_climbright
// 0x0042DB10: lara_col_climbstnc
// 0x0042DD20: lara_col_climbing
// ----------: lara_col_climbend
// 0x0042DE70: lara_col_climbdown
// 0x0042E010: LaraCheckForLetGo
// 0x0042E0C0: LaraTestClimb
// 0x0042E330: LaraTestClimbPos
// 0x0042E400: LaraDoClimbLeftRight
// 0x0042E4F0: LaraTestClimbUpPos
#endif // _H_INCLUDED
================================================
FILE: game/larafire.cpp
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/larafire.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_LaraFire() {
// INJECT(0x0042E740, LaraGun);
// INJECT(0x0042ECB0, CheckForHoldingState);
// INJECT(0x0042ECF0, InitialiseNewWeapon);
// INJECT(0x0042EE30, LaraTargetInfo);
// INJECT(0x0042EFD0, LaraGetNewTarget);
// INJECT(0x0042F1F0, find_target_point);
// INJECT(0x0042F2A0, AimWeapon);
// INJECT(0x0042F370, FireWeapon);
// INJECT(0x0042F6E0, HitTarget);
// INJECT(0x0042F780, SmashItem);
// INJECT(0x0042F7E0, WeaponObject);
}
================================================
FILE: game/larafire.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef LARA_FIRE_H_INCLUDED
#define LARA_FIRE_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define LaraGun ((void(__cdecl*)(void)) 0x0042E740)
#define CheckForHoldingState ((int(__cdecl*)(int)) 0x0042ECB0)
#define InitialiseNewWeapon ((void(__cdecl*)(void)) 0x0042ECF0)
#define LaraTargetInfo ((void(__cdecl*)(WEAPON_INFO*)) 0x0042EE30)
#define LaraGetNewTarget ((void(__cdecl*)(WEAPON_INFO*)) 0x0042EFD0)
#define find_target_point ((void(__cdecl*)(ITEM_INFO*,GAME_VECTOR*)) 0x0042F1F0)
#define AimWeapon ((void(__cdecl*)(WEAPON_INFO*,LARA_ARM*)) 0x0042F2A0)
#define FireWeapon ((int(__cdecl*)(int,ITEM_INFO*,ITEM_INFO*,__int16*)) 0x0042F370)
#define HitTarget ((void(__cdecl*)(ITEM_INFO*,GAME_VECTOR*,int)) 0x0042F6E0)
#define SmashItem ((void(__cdecl*)(__int16,int)) 0x0042F780)
#define WeaponObject ((int(__cdecl*)(int)) 0x0042F7E0)
#endif // LARA_FIRE_H_INCLUDED
================================================
FILE: game/laraflare.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/laraflare.h"
#include "3dsystem/3d_gen.h"
#include "3dsystem/scalespr.h"
#include "game/draw.h"
#include "specific/game.h"
#include "specific/output.h"
#include "global/vars.h"
#ifdef FEATURE_VIDEOFX_IMPROVED
extern DWORD AlphaBlendMode;
#endif // FEATURE_VIDEOFX_IMPROVED
void __cdecl DrawFlareInAir(ITEM_INFO *item) {
int rate;
__int16 *ptr[2];
GetFrames(item, ptr, &rate);
phd_PushMatrix();
phd_TranslateAbs(item->pos.x, item->pos.y, item->pos.z);
phd_RotYXZ(item->pos.rotY, item->pos.rotX, item->pos.rotZ);
int clip = S_GetObjectBounds(ptr[0]);
if( clip ) {
CalculateObjectLighting(item, ptr[0]);
phd_PutPolygons(MeshPtr[Objects[ID_FLARE_ITEM].meshIndex], clip);
if( CHK_ANY((DWORD)item->data, 0x8000) ) {
phd_TranslateRel(-6, 6, 80);
phd_RotX(-90 * PHD_DEGREE);
phd_RotY(2 * GetRandomDraw());
S_CalculateStaticLight(0x800);
phd_PutPolygons(MeshPtr[Objects[ID_FLARE_FIRE].meshIndex], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
if( AlphaBlendMode ) {
int shade = (GetRandomDraw() & 0xFFF) + 0x1000;
DWORD flags = GLOW_FLARE_COLOR;
flags |= SPR_BLEND_ADD|SPR_TINT|SPR_SHADE|SPR_SEMITRANS;
S_DrawSprite(flags, 0, 0, 0, Objects[ID_GLOW].meshIndex, shade, 0);
}
#endif // FEATURE_VIDEOFX_IMPROVED
}
}
phd_PopMatrix();
}
/*
* Inject function
*/
void Inject_LaraFlare() {
// INJECT(0x0042F840, DoFlareLight);
// INJECT(0x0042F8E0, DoFlareInHand);
INJECT(0x0042F9C0, DrawFlareInAir);
// INJECT(0x0042FAC0, CreateFlare);
// INJECT(0x0042FCA0, set_flare_arm);
// INJECT(0x0042FCF0, draw_flare);
// INJECT(0x0042FE60, undraw_flare);
// INJECT(0x00430090, draw_flare_meshes);
// INJECT(0x004300B0, undraw_flare_meshes);
// INJECT(0x004300D0, ready_flare);
// INJECT(0x00430110, FlareControl);
}
================================================
FILE: game/laraflare.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef LARA_FLARE_H_INCLUDED
#define LARA_FLARE_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x0042F840: DoFlareLight
// 0x0042F8E0: DoFlareInHand
void __cdecl DrawFlareInAir(ITEM_INFO *item);
// 0x0042FAC0: CreateFlare
// 0x0042FCA0: set_flare_arm
// 0x0042FCF0: draw_flare
// 0x0042FE60: undraw_flare
// 0x00430090: draw_flare_meshes
// 0x004300B0: undraw_flare_meshes
// 0x004300D0: ready_flare
// 0x00430110: FlareControl
#endif // LARA_FLARE_H_INCLUDED
================================================
FILE: game/laramisc.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/laramisc.h"
#include "3dsystem/phd_math.h"
#include "game/collide.h"
#include "game/control.h"
#include "game/effects.h"
#include "game/lara.h"
#include "game/larasurf.h"
#include "game/laraswim.h"
#include "game/invfunc.h"
#include "game/items.h"
#include "game/larafire.h"
#include "game/sound.h"
#include "specific/sndpc.h"
#include "global/vars.h"
void __cdecl LaraControl(__int16 itemID) {
COLL_INFO coll;
ITEM_INFO *item = LaraItem;
#ifdef FEATURE_CHEAT
if( CHK_ANY(GF_GameFlow.flags, GFF_EnableCheatCode|GFF_DozyCheatEnabled) ) {
// Recover health + get inventory stuff
if( CHK_ANY(InputStatus, IN_STUFFCHEAT) ) {
LaraCheatGetStuff();
item->hitPoints = 1000;
}
// Enable dozy cheat (flying with full health). It is allowed for boat, but not for skidoo
if( !Lara.extra_anim && (Lara.skidoo < 0 || Items[Lara.skidoo].objectID == ID_BOAT)
&& Lara.water_status != LWS_Cheat && CHK_ANY(InputStatus, IN_DOZYCHEAT) )
{
if( Lara.water_status != LWS_Underwater || item->hitPoints <= 0 ) {
item->pos.y -= 0x80;
item->animNumber = 87;
item->frameNumber = Anims[item->animNumber].frameBase;
item->currentAnimState = AS_SWIM;
item->goalAnimState = AS_SWIM;
item->gravity = 0;
item->pos.rotX = 30*PHD_DEGREE;
item->fallSpeed = 30;
Lara.torso_x_rot = Lara.torso_y_rot = 0;
Lara.head_x_rot = Lara.head_y_rot = 0;
if( item->hitPoints <= 0 && Lara.gun_type != LGT_Flare ) {
BYTE backupGunType = SaveGame.start[CurrentLevel].gunType;
SaveGame.start[CurrentLevel].gunType = Lara.request_gun_type;
LaraInitialiseMeshes(CurrentLevel);
SaveGame.start[CurrentLevel].gunType = backupGunType;
Lara.gun_status = LGS_Armless;
Lara.target = NULL;
memset(&Lara.left_arm, 0, sizeof(Lara.left_arm));
memset(&Lara.right_arm, 0, sizeof(Lara.right_arm));
if( Lara.weapon_item >= 0 ) {
KillItem(Lara.weapon_item);
Lara.weapon_item = -1;
}
}
}
Lara.water_status = LWS_Cheat;
Lara.skidoo = -1; // get off from vehicle
Lara.spaz_effect_count = 0;
Lara.spaz_effect = NULL;
Lara.hit_frame = 0;
Lara.hit_direction = -1;
Lara.air = 1800;
Lara.death_count = 0;
Lara.mesh_effects = 0x7FFF; // Lara has golden skin
}
}
#endif // FEATURE_CHEAT
BOOL isRoomUnderwater = CHK_ANY(RoomInfo[item->roomNumber].flags, ROOM_UNDERWATER);
int depth = GetWaterDepth(item->pos.x, item->pos.y, item->pos.z, item->roomNumber);
int height = GetWaterHeight(item->pos.x, item->pos.y, item->pos.z, item->roomNumber);
int water_surface_dist = ( height == NO_HEIGHT ) ? NO_HEIGHT : item->pos.y - height;
Lara.water_surface_dist = -water_surface_dist;
WadeSplash(item, height);
if( Lara.skidoo == -1 && !Lara.extra_anim ) {
switch( Lara.water_status ) {
case LWS_AboveWater:
if( water_surface_dist != NO_HEIGHT && water_surface_dist >= 0x180 ) {
if( depth <= (0x2DA-0x100) ) {
if( water_surface_dist > 0x180 ) {
Lara.water_status = LWS_Wade;
if( !item->gravity ) {
item->goalAnimState = AS_STOP;
}
}
} else if( isRoomUnderwater ) {
Lara.air = 1800;
Lara.water_status = LWS_Underwater;
item->gravity = 0;
item->pos.y += 100;
UpdateLaraRoom(item, 0);
StopSoundEffect(30);
switch( item->currentAnimState ) {
case AS_SWANDIVE:
item->pos.rotX = -45*PHD_DEGREE;
item->goalAnimState = AS_DIVE;
AnimateLara(item);
item->fallSpeed *= 2;
break;
case AS_FASTDIVE:
item->pos.rotX = -85*PHD_DEGREE;
item->goalAnimState = AS_DIVE;
AnimateLara(item);
item->fallSpeed *= 2;
break;
default:
item->pos.rotX = -45*PHD_DEGREE;
item->animNumber = 112;
item->frameNumber = Anims[item->animNumber].frameBase;
item->currentAnimState = AS_DIVE;
item->goalAnimState = AS_SWIM;
item->fallSpeed = item->fallSpeed * 3 / 2;
break;
}
Lara.torso_x_rot = Lara.torso_y_rot = 0;
Lara.head_x_rot = Lara.head_y_rot = 0;
Splash(item);
}
}
break;
case LWS_Wade:
Camera.targetElevation = -22*PHD_DEGREE;
if( water_surface_dist >= 0x180 ) {
if( water_surface_dist > 0x2DA ) {
Lara.water_status = LWS_Surface;
item->pos.y += 1 - water_surface_dist;
switch( item->currentAnimState ) {
case AS_BACK:
item->currentAnimState = AS_SURFBACK;
item->animNumber = 140;
item->frameNumber = Anims[item->animNumber].frameBase;
break;
case AS_STEPRIGHT:
item->currentAnimState = AS_SURFRIGHT;
item->animNumber = 144;
item->frameNumber = Anims[item->animNumber].frameBase;;
break;
case AS_STEPLEFT:
item->currentAnimState = AS_SURFLEFT;
item->animNumber = 143;
item->frameNumber = Anims[item->animNumber].frameBase;;
break;
default:
item->currentAnimState = AS_SURFSWIM;
item->animNumber = 116;
item->frameNumber = Anims[item->animNumber].frameBase;;
break;
}
item->gravity = 0;
item->goalAnimState = item->currentAnimState;
item->fallSpeed = 0;
Lara.dive_count = 0;
item->pos.rotX = item->pos.rotZ = 0;
Lara.torso_x_rot = Lara.torso_y_rot = 0;
Lara.head_x_rot = Lara.head_y_rot = 0;
UpdateLaraRoom(item, -0x17D);
}
} else {
Lara.water_status = LWS_AboveWater;
if( item->currentAnimState == AS_WADE )
item->goalAnimState = AS_RUN;
}
break;
case LWS_Surface:
if( !isRoomUnderwater ) {
if( water_surface_dist <= 0x180 ) {
Lara.water_status = LWS_AboveWater;
item->animNumber = 34;
item->frameNumber = Anims[item->animNumber].frameBase;;
item->goalAnimState = AS_FORWARDJUMP;
item->currentAnimState = AS_FORWARDJUMP;
item->gravity = 1;
item->speed = item->fallSpeed / 4;
} else {
Lara.water_status = LWS_Wade;
item->animNumber = 103;
item->currentAnimState = AS_STOP;
item->frameNumber = Anims[item->animNumber].frameBase;;
item->goalAnimState = AS_WADE;
AnimateItem(item);
}
item->fallSpeed = 0;
item->pos.rotX = item->pos.rotZ = 0;
Lara.torso_x_rot = Lara.torso_y_rot = 0;
Lara.head_x_rot = Lara.head_y_rot = 0;
}
break;
case LWS_Underwater:
if( !isRoomUnderwater ) {
if( depth == NO_HEIGHT || ABS(water_surface_dist) >= 0x100 ) {
Lara.water_status = LWS_AboveWater;
item->animNumber = 34;
item->frameNumber = Anims[item->animNumber].frameBase;;
item->goalAnimState = AS_FORWARDJUMP;
item->currentAnimState = AS_FORWARDJUMP;
item->speed = item->fallSpeed / 4;
item->gravity = 1;
item->fallSpeed = 0;
item->pos.rotX = item->pos.rotZ = 0;
Lara.torso_x_rot = Lara.torso_y_rot = 0;
Lara.head_x_rot = Lara.head_y_rot = 0;
} else {
Lara.water_status = LWS_Surface;
item->pos.y += 1 - water_surface_dist;
item->animNumber = 114;
item->frameNumber = Anims[item->animNumber].frameBase;;
item->goalAnimState = AS_SURFTREAD;
item->currentAnimState = AS_SURFTREAD;
item->fallSpeed = 0;
Lara.dive_count = 11;
item->pos.rotX = item->pos.rotZ = 0;
Lara.torso_x_rot = Lara.torso_y_rot = 0;
Lara.head_x_rot = Lara.head_y_rot = 0;
UpdateLaraRoom(item, -0x17D);
PlaySoundEffect(36, &item->pos, SFX_ALWAYS);
}
}
break;
default:
break;
}
}
if( item->hitPoints > 0 ) {
if( GF_NoFloor && item->pos.y >= GF_NoFloor ) {
item->hitPoints = -1;
Lara.death_count = 9*30; // let's skip 9 seconds to death
}
} else {
item->hitPoints = -1;
if( !Lara.death_count ) {
S_CDStop();
}
++Lara.death_count;
if( CHK_ANY(item->flags, IFL_INVISIBLE) ) { // Nothing left from Lara?
++Lara.death_count; // In this case death timer ticks twice faster
return;
}
}
switch( Lara.water_status ) {
case LWS_AboveWater:
case LWS_Wade:
Lara.air = 1800;
LaraAboveWater(item, &coll);
break;
case LWS_Underwater:
if( item->hitPoints >= 0 && --Lara.air < 0 ) {
Lara.air = -1;
item->hitPoints -= 5;
}
LaraUnderWater(item, &coll);
break;
case LWS_Surface:
if( item->hitPoints >= 0 ) {
Lara.air += 10;
CLAMPG(Lara.air, 1800)
}
LaraSurface(item, &coll);
break;
#ifdef FEATURE_CHEAT
case LWS_Cheat:
if( CHK_ANY(GF_GameFlow.flags, GFF_EnableCheatCode|GFF_DozyCheatEnabled) ) {
// Update Dozy state just in case
item->hitPoints = 1000;
Lara.death_count = 0; // NOTE: if died already, forget about death
LaraUnderWater(item, &coll);
// Return Lara to normal state if Walk is pressed without Look
if( !Lara.extra_anim && CHK_ANY(InputStatus, IN_SLOW) && !CHK_ANY(InputStatus, IN_LOOK) ) {
if( isRoomUnderwater || (water_surface_dist != NO_HEIGHT && water_surface_dist > 0) ) {
Lara.water_status = LWS_Underwater;
} else {
Lara.water_status = LWS_AboveWater;
item->animNumber = 11;
item->frameNumber = Anims[item->animNumber].frameBase;
item->pos.rotX = item->pos.rotZ = 0;
Lara.torso_x_rot = Lara.torso_y_rot = 0;
Lara.head_x_rot = Lara.head_y_rot = 0;
}
Lara.mesh_effects = 0;
}
}
break;
#endif // FEATURE_CHEAT
default:
break;
}
SaveGame.statistics.distance += phd_sqrt((item->pos.z - Lara.last_pos.z) * (item->pos.z - Lara.last_pos.z)
+ (item->pos.y - Lara.last_pos.y) * (item->pos.y - Lara.last_pos.y)
+ (item->pos.x - Lara.last_pos.x) * (item->pos.x - Lara.last_pos.x));
Lara.last_pos.x = item->pos.x;
Lara.last_pos.y = item->pos.y;
Lara.last_pos.z = item->pos.z;
}
void __cdecl UseItem(__int16 itemID) {
if( itemID <= ID_NONE || itemID >= ID_NUMBER_OBJECTS )
return;
switch( itemID ) {
case ID_PISTOL_ITEM:
case ID_PISTOL_OPTION:
Lara.request_gun_type = LGT_Pistols;
break;
case ID_MAGNUM_ITEM:
case ID_MAGNUM_OPTION:
Lara.request_gun_type = LGT_Magnums;
break;
case ID_UZI_ITEM:
case ID_UZI_OPTION:
Lara.request_gun_type = LGT_Uzis;
break;
case ID_SHOTGUN_ITEM:
case ID_SHOTGUN_OPTION:
Lara.request_gun_type = LGT_Shotgun;
break;
case ID_HARPOON_ITEM:
case ID_HARPOON_OPTION:
Lara.request_gun_type = LGT_Harpoon;
break;
case ID_M16_ITEM:
case ID_M16_OPTION:
Lara.request_gun_type = LGT_M16;
break;
case ID_GRENADE_ITEM:
case ID_GRENADE_OPTION:
Lara.request_gun_type = LGT_Grenade;
break;
case ID_FLARES_ITEM:
case ID_FLARES_OPTION:
Lara.request_gun_type = LGT_Flare;
break;
case ID_SMALL_MEDIPACK_ITEM:
case ID_SMALL_MEDIPACK_OPTION:
if( LaraItem->hitPoints > 0 && LaraItem->hitPoints < 1000 ) {
LaraItem->hitPoints += 500;
if( LaraItem->hitPoints > 1000 )
LaraItem->hitPoints = 1000;
Inv_RemoveItem(ID_SMALL_MEDIPACK_ITEM);
PlaySoundEffect(116, NULL, 2);
++SaveGame.statistics.mediPacks;
}
break;
case ID_LARGE_MEDIPACK_ITEM:
case ID_LARGE_MEDIPACK_OPTION:
if( LaraItem->hitPoints > 0 && LaraItem->hitPoints < 1000 ) {
LaraItem->hitPoints += 1000;
if( LaraItem->hitPoints > 1000 )
LaraItem->hitPoints = 1000;
Inv_RemoveItem(ID_LARGE_MEDIPACK_ITEM);
PlaySoundEffect(116, NULL, 2);
SaveGame.statistics.mediPacks += 2;
}
break;
}
}
void __cdecl LaraCheatGetStuff() {
// NOTE: there is no availability checks in the original code
if( Objects[ID_PISTOL_OPTION].loaded ) {
if( !Inv_RequestItem(ID_PISTOL_ITEM) ) {
Inv_AddItem(ID_PISTOL_ITEM);
}
}
if( Objects[ID_MAGNUM_OPTION].loaded ) {
if( !Inv_RequestItem(ID_MAGNUM_ITEM) ) {
Inv_AddItem(ID_MAGNUM_ITEM);
}
Lara.magnum_ammo = (SaveGame.bonusFlag && !IsDemoLevelType) ? 10001 : 1000;
}
if( Objects[ID_UZI_OPTION].loaded ) {
if( !Inv_RequestItem(ID_UZI_ITEM) ) {
Inv_AddItem(ID_UZI_ITEM);
}
Lara.uzi_ammo = (SaveGame.bonusFlag && !IsDemoLevelType) ? 10001 : 2000;
}
if( Objects[ID_SHOTGUN_OPTION].loaded ) {
if( !Inv_RequestItem(ID_SHOTGUN_ITEM) ) {
Inv_AddItem(ID_SHOTGUN_ITEM);
}
Lara.shotgun_ammo = (SaveGame.bonusFlag && !IsDemoLevelType) ? 10001 : 300;
}
if( Objects[ID_HARPOON_OPTION].loaded ) {
if( !Inv_RequestItem(ID_HARPOON_ITEM) ) {
Inv_AddItem(ID_HARPOON_ITEM);
}
Lara.harpoon_ammo = (SaveGame.bonusFlag && !IsDemoLevelType) ? 10001 : 300;
}
if( Objects[ID_M16_OPTION].loaded ) {
if( !Inv_RequestItem(ID_M16_ITEM) ) {
Inv_AddItem(ID_M16_ITEM);
}
Lara.m16_ammo = (SaveGame.bonusFlag && !IsDemoLevelType) ? 10001 : 300;
}
if( Objects[ID_GRENADE_OPTION].loaded ) {
if( !Inv_RequestItem(ID_GRENADE_ITEM) ) {
Inv_AddItem(ID_GRENADE_ITEM);
}
Lara.grenade_ammo = (SaveGame.bonusFlag && !IsDemoLevelType) ? 10001 : 300;
}
for( int i = 0; i < 10; ++i ) {
// NOTE: there are no limits in the original code, but it works wrong without limits
if( Objects[ID_FLARES_OPTION].loaded && Inv_RequestItem(ID_FLARE_ITEM) < 240 ) {
Inv_AddItem(ID_FLARES_ITEM);
}
if( Objects[ID_SMALL_MEDIPACK_OPTION].loaded && Inv_RequestItem(ID_SMALL_MEDIPACK_ITEM) < 240 ) {
Inv_AddItem(ID_SMALL_MEDIPACK_ITEM);
}
if( Objects[ID_LARGE_MEDIPACK_OPTION].loaded && Inv_RequestItem(ID_LARGE_MEDIPACK_ITEM) < 240 ) {
Inv_AddItem(ID_LARGE_MEDIPACK_ITEM);
}
}
#ifdef FEATURE_CHEAT
// NOTE: there is no cheat for keys/puzzles in the original code
if( Objects[ID_KEY_OPTION1].loaded && !Inv_RequestItem(ID_KEY_ITEM1) ) {
Inv_AddItem(ID_KEY_ITEM1);
}
if( Objects[ID_KEY_OPTION2].loaded && !Inv_RequestItem(ID_KEY_ITEM2) ) {
Inv_AddItem(ID_KEY_ITEM2);
}
if( Objects[ID_KEY_OPTION3].loaded && !Inv_RequestItem(ID_KEY_ITEM3) ) {
Inv_AddItem(ID_KEY_ITEM3);
}
if( Objects[ID_KEY_OPTION4].loaded && !Inv_RequestItem(ID_KEY_ITEM4) ) {
Inv_AddItem(ID_KEY_ITEM4);
}
if( Objects[ID_PUZZLE_OPTION1].loaded && !Inv_RequestItem(ID_PUZZLE_ITEM1) ) {
Inv_AddItem(ID_PUZZLE_ITEM1);
}
if( Objects[ID_PUZZLE_OPTION2].loaded && !Inv_RequestItem(ID_PUZZLE_ITEM2) ) {
Inv_AddItem(ID_PUZZLE_ITEM2);
}
if( Objects[ID_PUZZLE_OPTION3].loaded && !Inv_RequestItem(ID_PUZZLE_ITEM3) ) {
Inv_AddItem(ID_PUZZLE_ITEM3);
}
if( Objects[ID_PUZZLE_OPTION4].loaded && !Inv_RequestItem(ID_PUZZLE_ITEM4) ) {
Inv_AddItem(ID_PUZZLE_ITEM4);
}
if( Objects[ID_PICKUP_OPTION1].loaded && !Inv_RequestItem(ID_PICKUP_ITEM1) ) {
Inv_AddItem(ID_PICKUP_ITEM1);
}
if( Objects[ID_PICKUP_OPTION2].loaded && !Inv_RequestItem(ID_PICKUP_ITEM2) ) {
Inv_AddItem(ID_PICKUP_ITEM2);
}
#endif // FEATURE_CHEAT
}
void __cdecl ControlLaraExtra(__int16 itemID) {
AnimateItem(&Items[itemID]);
}
void __cdecl InitialiseLaraLoad(__int16 itemID) {
Lara.item_number = itemID;
LaraItem = &Items[itemID];
}
void __cdecl InitialiseLaraInventory(int levelID) {
int i;
START_INFO *start = &SaveGame.start[levelID];
Inv_RemoveAllItems();
if( GF_RemoveWeapons ) {
start->has_pistols = 0;
start->has_magnums = 0;
start->has_uzis = 0;
start->has_shotgun = 0;
start->has_m16 = 0;
start->has_grenade = 0;
start->has_harpoon = 0;
start->gunType = LGT_Unarmed;
start->gunStatus = LGS_Armless;
GF_RemoveWeapons = 0;
}
if( GF_RemoveAmmo ) {
start->m16Ammo = 0;
start->grenadeAmmo = 0;
start->harpoonAmmo = 0;
start->shotgunAmmo = 0;
start->uziAmmo = 0;
start->magnumAmmo = 0;
start->pistolAmmo = 0;
start->flares = 0;
start->largeMedipacks = 0;
start->smallMedipacks = 0;
GF_RemoveAmmo = 0;
}
// NOTE: additional weapon availability checks not presented in the original game
if( !Objects[ID_PISTOL_OPTION].loaded ) {
start->has_pistols = 0;
}
if( !Objects[ID_UZI_OPTION].loaded ) {
start->has_uzis = 0;
}
if( !Objects[ID_MAGNUM_OPTION].loaded ) {
start->has_magnums = 0;
}
if( !Objects[ID_SHOTGUN_OPTION].loaded ) {
start->has_shotgun = 0;
}
if( !Objects[ID_GRENADE_OPTION].loaded ) {
start->has_grenade = 0;
}
if( !Objects[ID_M16_OPTION].loaded ) {
start->has_m16 = 0;
}
if( !Objects[ID_HARPOON_OPTION].loaded ) {
start->has_harpoon = 0;
}
// NOTE: additional ammo availability checks not presented in the original game
if( !start->has_pistols && !Objects[ID_PISTOL_AMMO_OPTION].loaded ) {
start->pistolAmmo = 0;
}
if( !start->has_uzis && !Objects[ID_UZI_AMMO_OPTION].loaded ) {
start->uziAmmo = 0;
}
if( !start->has_magnums && !Objects[ID_MAGNUM_AMMO_OPTION].loaded ) {
start->magnumAmmo = 0;
}
if( !start->has_shotgun && !Objects[ID_SHOTGUN_AMMO_OPTION].loaded ) {
start->shotgunAmmo = 0;
}
if( !start->has_grenade && !Objects[ID_GRENADE_AMMO_OPTION].loaded ) {
start->grenadeAmmo = 0;
}
if( !start->has_m16 && !Objects[ID_M16_AMMO_OPTION].loaded ) {
start->m16Ammo = 0;
}
if( !start->has_harpoon && !Objects[ID_HARPOON_AMMO_OPTION].loaded ) {
start->harpoonAmmo = 0;
}
// NOTE: additional start gun type availability checks not presented in the original game
switch( start->gunType ) {
// if the current weapon is absent then fall through
case LGT_Pistols : if( start->has_pistols ) break; // fall through
case LGT_Magnums : if( start->has_magnums ) break; // fall through
case LGT_Uzis : if( start->has_uzis ) break; // fall through
case LGT_Shotgun : if( start->has_shotgun ) break; // fall through
case LGT_M16 : if( start->has_m16 ) break; // fall through
case LGT_Grenade : if( start->has_grenade ) break; // fall through
case LGT_Harpoon : if( start->has_harpoon ) break; // fall through
start->gunType = start->has_pistols ? LGT_Pistols : LGT_Unarmed;
break;
default :
break;
}
// Statistics
Inv_AddItem(ID_COMPASS_ITEM);
// Pistols
Lara.pistol_ammo = 1000;
if( start->has_pistols ) {
Inv_AddItem(ID_PISTOL_ITEM);
}
// Magnums
if( start->has_magnums ) {
Inv_AddItem(ID_MAGNUM_ITEM);
Lara.magnum_ammo = start->magnumAmmo;
GlobalItemReplace(ID_MAGNUM_ITEM, ID_MAGNUM_AMMO_ITEM);
} else {
for( i = 0; i < start->magnumAmmo / 40; ++i ) {
Inv_AddItem(ID_MAGNUM_AMMO_ITEM);
}
Lara.magnum_ammo = 0;
}
// Uzis
if( start->has_uzis ) {
Inv_AddItem(ID_UZI_ITEM);
Lara.uzi_ammo = start->uziAmmo;
GlobalItemReplace(ID_UZI_ITEM, ID_UZI_AMMO_ITEM);
} else {
for( i = 0; i < start->uziAmmo / 80; ++i ) {
Inv_AddItem(ID_UZI_AMMO_ITEM);
}
Lara.uzi_ammo = 0;
}
// Shotgun
if( start->has_shotgun ) {
Inv_AddItem(ID_SHOTGUN_ITEM);
Lara.shotgun_ammo = start->shotgunAmmo;
GlobalItemReplace(ID_SHOTGUN_ITEM, ID_SHOTGUN_AMMO_ITEM);
} else {
for( i = 0; i < start->shotgunAmmo / 12; ++i ) {
Inv_AddItem(ID_SHOTGUN_AMMO_ITEM);
}
Lara.shotgun_ammo = 0;
}
// Grenade
if( start->has_grenade ) {
Inv_AddItem(ID_GRENADE_ITEM);
Lara.grenade_ammo = start->grenadeAmmo;
GlobalItemReplace(ID_GRENADE_ITEM, ID_GRENADE_AMMO_ITEM);
} else {
for( i = 0; i < start->grenadeAmmo / 2; ++i ) {
Inv_AddItem(ID_GRENADE_AMMO_ITEM);
}
Lara.grenade_ammo = 0;
}
// M16
if( start->has_m16 ) {
Inv_AddItem(ID_M16_ITEM);
Lara.m16_ammo = start->m16Ammo;
GlobalItemReplace(ID_M16_ITEM, ID_M16_AMMO_ITEM);
} else {
for( i = 0; i < start->m16Ammo / 40; ++i ) {
Inv_AddItem(ID_M16_AMMO_ITEM);
}
Lara.m16_ammo = 0;
}
// Harpoon
if( start->has_harpoon ) {
Inv_AddItem(ID_HARPOON_ITEM);
Lara.harpoon_ammo = start->harpoonAmmo;
GlobalItemReplace(ID_HARPOON_ITEM, ID_HARPOON_AMMO_ITEM);
} else {
for( i = 0; i < start->harpoonAmmo / 3; ++i ) {
Inv_AddItem(ID_HARPOON_AMMO_ITEM);
}
Lara.harpoon_ammo = 0;
}
// Flares
for( i = 0; i < start->flares; ++i ) {
Inv_AddItem(ID_FLARE_ITEM);
}
// Small medipacks
for( i = 0; i < start->smallMedipacks; ++i ) {
Inv_AddItem(ID_SMALL_MEDIPACK_ITEM);
}
// Large medipacks
for( i = 0; i < start->largeMedipacks; ++i ) {
Inv_AddItem(ID_LARGE_MEDIPACK_ITEM);
}
Lara.gun_status = LGS_Armless;
Lara.last_gun_type = start->gunType;
Lara.gun_type = start->gunType;
Lara.request_gun_type = start->gunType;
LaraInitialiseMeshes(levelID);
InitialiseNewWeapon();
}
/*
* Inject function
*/
void Inject_LaraMisc() {
INJECT(0x00430380, LaraControl);
// INJECT(0x00430A10, AnimateLara);
INJECT(0x00430D10, UseItem);
INJECT(0x00430ED0, LaraCheatGetStuff);
INJECT(0x00430F90, ControlLaraExtra);
INJECT(0x00430FB0, InitialiseLaraLoad);
// INJECT(0x00430FE0, InitialiseLara);
INJECT(0x004312A0, InitialiseLaraInventory);
// INJECT(0x00431610, LaraInitialiseMeshes);
}
================================================
FILE: game/laramisc.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef LARAMISC_H_INCLUDED
#define LARAMISC_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl LaraControl(__int16 itemID);
#define AnimateLara ((void(__cdecl*)(ITEM_INFO*)) 0x00430A10)
void __cdecl UseItem(__int16 itemID); // 0x00430D10
void __cdecl LaraCheatGetStuff(); // 0x00430ED0
void __cdecl ControlLaraExtra(__int16 itemID); // 0x00430F90
void __cdecl InitialiseLaraLoad(__int16 itemID); // 0x00430FB0
#define InitialiseLara ((void(__cdecl*)(int)) 0x00430FE0)
void __cdecl InitialiseLaraInventory(int levelID); // 0x004312A0
#define LaraInitialiseMeshes ((void(__cdecl*)(int)) 0x00431610)
#endif // LARAMISC_H_INCLUDED
================================================
FILE: game/larasurf.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/larasurf.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_LaraSurf() {
// INJECT(0x00431710, LaraSurface);
// INJECT(0x00431870, lara_as_surfswim);
// INJECT(0x004318E0, lara_as_surfback);
// INJECT(0x00431940, lara_as_surfleft);
// INJECT(0x004319A0, lara_as_surfright);
// INJECT(0x00431A00, lara_as_surftread);
// INJECT(0x00431AC0, lara_col_surfswim);
// INJECT(0x00431B00, lara_col_surfback);
// INJECT(0x00431B30, lara_col_surfleft);
// INJECT(0x00431B60, lara_col_surfright);
// INJECT(0x00431B90, lara_col_surftread);
// INJECT(0x00431BF0, LaraSurfaceCollision);
// INJECT(0x00431CF0, LaraTestWaterStepOut);
// INJECT(0x00431DE0, LaraTestWaterClimbOut);
}
================================================
FILE: game/larasurf.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef LARA_SURF_H_INCLUDED
#define LARA_SURF_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define LaraSurface ((void(__cdecl*)(ITEM_INFO*, COLL_INFO*)) 0x00431710)
// 0x00431870: lara_as_surfswim
// 0x004318E0: lara_as_surfback
// 0x00431940: lara_as_surfleft
// 0x004319A0: lara_as_surfright
// 0x00431A00: lara_as_surftread
// 0x00431AC0: lara_col_surfswim
// 0x00431B00: lara_col_surfback
// 0x00431B30: lara_col_surfleft
// 0x00431B60: lara_col_surfright
// 0x00431B90: lara_col_surftread
// 0x00431BF0: LaraSurfaceCollision
// 0x00431CF0: LaraTestWaterStepOut
// 0x00431DE0: LaraTestWaterClimbOut
#endif // LARA_SURF_H_INCLUDED
================================================
FILE: game/laraswim.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/laraswim.h"
#include "global/vars.h"
void __cdecl SwimTurn(ITEM_INFO *item) {
if( CHK_ANY(InputStatus, IN_FORWARD) ) {
item->pos.rotX -= 2*PHD_DEGREE;
} else if( CHK_ANY(InputStatus, IN_BACK) ) {
item->pos.rotX += 2*PHD_DEGREE;
}
if( CHK_ANY(InputStatus, IN_LEFT) ) {
Lara.turn_rate -= PHD_DEGREE*9/4;
CLAMPL(Lara.turn_rate, -6*PHD_DEGREE);
item->pos.rotZ -= 3*PHD_DEGREE;
} else if( CHK_ANY(InputStatus, IN_RIGHT) ) {
Lara.turn_rate += PHD_DEGREE*9/4;
CLAMPG(Lara.turn_rate, 6*PHD_DEGREE);
item->pos.rotZ += 3*PHD_DEGREE;
}
}
void __cdecl lara_as_swim(ITEM_INFO *item, COLL_INFO *coll) {
if( item->hitPoints <= 0 ) {
item->goalAnimState = AS_UWDEATH;
return;
}
if( CHK_ANY(InputStatus, IN_ROLL) ) {
item->currentAnimState = AS_WATERROLL;
item->animNumber = 203;
item->frameNumber = Anims[item->animNumber].frameBase;
} else {
SwimTurn(item);
item->fallSpeed += 8;
#ifdef FEATURE_CHEAT
if( Lara.water_status == LWS_Cheat ) {
CLAMPG(item->fallSpeed, 400);
} else {
CLAMPG(item->fallSpeed, 200);
}
#else // FEATURE_CHEAT
CLAMPG(item->fallSpeed, 200);
#endif // FEATURE_CHEAT
if( !CHK_ANY(InputStatus, IN_JUMP) ) {
item->goalAnimState = AS_GLIDE;
}
}
}
/*
* Inject function
*/
void Inject_LaraSwim() {
// INJECT(0x00432000, LaraUnderWater);
INJECT(0x00432230, SwimTurn);
INJECT(0x004322C0, lara_as_swim);
// INJECT(0x00432330, lara_as_glide);
// INJECT(0x004323B0, lara_as_tread);
// INJECT(0x00432440, lara_as_dive);
// INJECT(0x00432460, lara_as_uwdeath);
// INJECT(0x004324C0, lara_as_waterroll);
// INJECT(0x004324D0, lara_col_swim);
// INJECT(----------, lara_col_glide);
// INJECT(----------, lara_col_tread);
// INJECT(----------, lara_col_dive);
// INJECT(0x004324F0, lara_col_uwdeath);
// INJECT(----------, lara_col_waterroll);
// INJECT(0x00432550, GetWaterDepth);
// INJECT(0x004326F0, LaraTestWaterDepth);
// INJECT(0x004327C0, LaraSwimCollision);
// INJECT(0x00432920, LaraWaterCurrent);
}
================================================
FILE: game/laraswim.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef LARA_SWIM_H_INCLUDED
#define LARA_SWIM_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define LaraUnderWater ((void(__cdecl*)(ITEM_INFO*, COLL_INFO*)) 0x00432000)
void __cdecl SwimTurn(ITEM_INFO *item); // 0x00432230
void __cdecl lara_as_swim(ITEM_INFO *item, COLL_INFO *coll); // 0x004322C0
// 0x00432330: lara_as_glide
// 0x004323B0: lara_as_tread
// 0x00432440: lara_as_dive
// 0x00432460: lara_as_uwdeath
// 0x004324C0: lara_as_waterroll
// 0x004324D0: lara_col_swim
// ----------: lara_col_glide
// ----------: lara_col_tread
// ----------: lara_col_dive
// 0x004324F0: lara_col_uwdeath
// ----------: lara_col_waterroll
#define GetWaterDepth ((int(__cdecl*)(int, int, int, __int16)) 0x00432550)
// 0x004326F0: LaraTestWaterDepth
// 0x004327C0: LaraSwimCollision
// 0x00432920: LaraWaterCurrent
#endif // LARA_SWIM_H_INCLUDED
================================================
FILE: game/lot.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/lot.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Lot() {
// INJECT(0x00432B10, InitialiseLOTarray);
// INJECT(0x00432B70, DisableBaddieAI);
// INJECT(0x00432BC0, EnableBaddieAI);
// INJECT(0x00432D70, InitialiseSlot);
// INJECT(0x00432F80, CreateZone);
// INJECT(0x00433040, ClearLOT);
}
================================================
FILE: game/lot.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef LOT_H_INCLUDED
#define LOT_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x00432B10: InitialiseLOTarray
#define DisableBaddieAI ((void(__cdecl*)(__int16)) 0x00432B70)
#define EnableBaddieAI ((int(__cdecl*)(__int16, BOOL)) 0x00432BC0)
// 0x00432D70: InitialiseSlot
// 0x00432F80: CreateZone
// 0x00433040: ClearLOT
#endif // LOT_H_INCLUDED
================================================
FILE: game/missile.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/missile.h"
#include "3dsystem/phd_math.h"
#include "game/control.h"
#include "game/draw.h"
#include "game/effects.h"
#include "game/items.h"
#include "game/sound.h"
#include "game/traps.h"
#include "specific/game.h"
#include "global/vars.h"
void __cdecl ControlMissile(__int16 fxID) {
FX_INFO *fx = &Effects[fxID];
if( Effects[fxID].object_number == ID_MISSILE_HARPOON
&& !CHK_ANY(RoomInfo[fx->room_number].flags, ROOM_UNDERWATER)
&& fx->pos.rotX > -PHD_90*3/4 )
{
fx->pos.rotX -= PHD_DEGREE;
}
int speed = fx->speed * phd_cos(fx->pos.rotX) >> W2V_SHIFT;
fx->pos.y += fx->speed * phd_sin(-fx->pos.rotX) >> W2V_SHIFT;
fx->pos.z += speed * phd_cos(fx->pos.rotY) >> W2V_SHIFT;
fx->pos.x += speed * phd_sin(fx->pos.rotY) >> W2V_SHIFT;
__int16 roomNumber = fx->room_number;
FLOOR_INFO *floor = GetFloor(fx->pos.x, fx->pos.y, fx->pos.z, &roomNumber);
if( fx->pos.y >= GetHeight(floor, fx->pos.x, fx->pos.y, fx->pos.z)
|| (fx->pos.y <= GetCeiling(floor, fx->pos.x, fx->pos.y, fx->pos.z)) )
{
if( fx->object_number == ID_MISSILE_KNIFE || fx->object_number == ID_MISSILE_HARPOON ) {
fx->speed = 0;
fx->frame_number = -GetRandomControl()/11000;
fx->counter = 6;
fx->object_number = ID_RICOCHET;
PlaySoundEffect(258, &fx->pos, 0);
} else if( fx->object_number == ID_MISSILE_FLAME ) {
AddDynamicLight(fx->pos.x, fx->pos.y, fx->pos.z, 14, 11);
KillEffect(fxID);
}
return;
}
if( roomNumber != fx->room_number ) {
EffectNewRoom(fxID, roomNumber);
}
if( fx->object_number == ID_MISSILE_FLAME ) {
if( ItemNearLara(&fx->pos, 350) ) {
LaraItem->hitPoints -= 3;
LaraItem->hit_status = 1;
LaraBurn();
return;
}
} else if( ItemNearLara(&fx->pos, 200) ) {
LaraItem->hit_status = 1;
fx->pos.rotY = LaraItem->pos.rotY;
fx->counter = 0;
fx->speed = LaraItem->speed;
fx->frame_number = 0;
if( fx->object_number == ID_MISSILE_KNIFE || fx->object_number == ID_MISSILE_HARPOON ) {
LaraItem->hitPoints -= 50;
#ifdef FEATURE_CHEAT
if( Lara.water_status == LWS_Cheat ) {
fx->frame_number = -GetRandomControl()/11000;
fx->counter = 6;
fx->object_number = ID_RICOCHET;
PlaySoundEffect(258, &fx->pos, 0);
} else {
fx->object_number = ID_BLOOD;
PlaySoundEffect(317, &fx->pos, 0);
}
#else // FEATURE_CHEAT
fx->object_number = ID_BLOOD;
PlaySoundEffect(317, &fx->pos, 0);
#endif // FEATURE_CHEAT
}
}
if( fx->object_number == ID_MISSILE_HARPOON && CHK_ANY(RoomInfo[fx->room_number].flags, ROOM_UNDERWATER) ) {
CreateBubble(&fx->pos, fx->room_number);
} else if( fx->object_number == ID_MISSILE_FLAME && !fx->counter-- ) {
AddDynamicLight(fx->pos.x, fx->pos.y, fx->pos.z, 14, 11);
PlaySoundEffect(305, &fx->pos, 0);
KillEffect(fxID);
} else if( fx->object_number == ID_MISSILE_KNIFE ) {
fx->pos.rotZ += 30*PHD_DEGREE;
}
}
/*
* Inject function
*/
void Inject_Missile() {
INJECT(0x00433090, ControlMissile);
// INJECT(0x00433360, ShootAtLara);
// INJECT(0x00433410, ExplodingDeath);
// INJECT(0x004337A0, ControlBodyPart);
}
================================================
FILE: game/missile.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef MISSILE_H_INCLUDED
#define MISSILE_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl ControlMissile(__int16 fxID); // 0x00433090
// 0x00433360: ShootAtLara
#define ExplodingDeath ((BOOL(__cdecl*)(__int16, DWORD, __int16)) 0x00433410)
// 0x004337A0: ControlBodyPart
#endif // MISSILE_H_INCLUDED
================================================
FILE: game/moveblock.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/moveblock.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_MoveBlock() {
// INJECT(0x004339A0, InitialiseMovingBlock);
// INJECT(0x004339D0, MovableBlock);
// INJECT(0x00433B20, MovableBlockCollision);
// INJECT(0x00433D80, TestBlockMovable);
// INJECT(0x00433DD0, TestBlockPush);
// INJECT(0x00433F20, TestBlockPull);
// INJECT(0x00434160, AlterFloorHeight);
// INJECT(0x00434220, DrawMovableBlock);
// INJECT(0x00434250, DrawUnclippedItem);
}
================================================
FILE: game/moveblock.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef MOVE_BLOCK_H_INCLUDED
#define MOVE_BLOCK_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x004339A0: InitialiseMovingBlock
#define MovableBlock ((void(__cdecl*)(__int16)) 0x004339D0)
// 0x00433B20: MovableBlockCollision
// 0x00433D80: TestBlockMovable
// 0x00433DD0: TestBlockPush
// 0x00433F20: TestBlockPull
#define AlterFloorHeight ((void(__cdecl*)(ITEM_INFO *, int)) 0x00434160)
// 0x00434220: DrawMovableBlock
#define DrawUnclippedItem ((void(__cdecl*)(ITEM_INFO *)) 0x00434250)
#endif // MOVE_BLOCK_H_INCLUDED
================================================
FILE: game/objects.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/objects.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Objects() {
// INJECT(0x004342C0, EarthQuake);
// INJECT(0x004343A0, ControlCutShotgun);
// INJECT(0x004343E0, InitialiseFinalLevel);
// INJECT(0x004344B0, FinalLevelCounter);
// INJECT(0x004346C0, MiniCopterControl);
// INJECT(0x004347A0, InitialiseDyingMonk);
// INJECT(0x00434820, DyingMonk);
// INJECT(0x004348B0, ControlGongBonger);
// INJECT(0x00434970, DeathSlideCollision);
// INJECT(0x00434A30, ControlDeathSlide);
// INJECT(0x00434CC0, BigBowlControl);
// INJECT(0x00434DB0, BellControl);
// INJECT(0x00434E30, InitialiseWindow);
// INJECT(0x00434EB0, SmashWindow);
// INJECT(0x00434F80, WindowControl);
// INJECT(0x00435020, SmashIceControl);
// INJECT(0x00435100, ShutThatDoor);
// INJECT(0x00435150, OpenThatDoor);
// INJECT(0x00435190, InitialiseDoor);
// INJECT(0x00435570, DoorControl);
// INJECT(0x00435640, OnDrawBridge);
// INJECT(0x00435700, DrawBridgeFloor);
// INJECT(0x00435740, DrawBridgeCeiling);
// INJECT(0x00435780, DrawBridgeCollision);
// INJECT(0x004357B0, InitialiseLift);
// INJECT(0x004357F0, LiftControl);
// INJECT(0x004358D0, LiftFloorCeiling);
// INJECT(0x00435A50, LiftFloor);
// INJECT(0x00435A90, LiftCeiling);
// INJECT(0x00435AD0, BridgeFlatFloor);
// INJECT(0x00435AF0, BridgeFlatCeiling);
// INJECT(0x00435B10, GetOffset);
// INJECT(0x00435B50, BridgeTilt1Floor);
// INJECT(0x00435B80, BridgeTilt1Ceiling);
// INJECT(0x00435BC0, BridgeTilt2Floor);
// INJECT(0x00435BF0, BridgeTilt2Ceiling);
// INJECT(0x00435C30, CopterControl);
// INJECT(0x00435D40, GeneralControl);
// INJECT(0x00435E20, DetonatorControl);
}
================================================
FILE: game/objects.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef OBJECTS_H_INCLUDED
#define OBJECTS_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x004342C0: EarthQuake
// 0x004343A0: ControlCutShotgun
// 0x004343E0: InitialiseFinalLevel
// 0x004344B0: FinalLevelCounter
// 0x004346C0: MiniCopterControl
// 0x004347A0: InitialiseDyingMonk
// 0x00434820: DyingMonk
// 0x004348B0: ControlGongBonger
// 0x00434970: DeathSlideCollision
// 0x00434A30: ControlDeathSlide
// 0x00434CC0: BigBowlControl
// 0x00434DB0: BellControl
// 0x00434E30: InitialiseWindow
#define SmashWindow ((void(__cdecl*)(__int16)) 0x00434EB0)
// 0x00434F80: WindowControl
// 0x00435020: SmashIceControl
// 0x00435100: ShutThatDoor
// 0x00435150: OpenThatDoor
// 0x00435190: InitialiseDoor
// 0x00435570: DoorControl
// 0x00435640: OnDrawBridge
// 0x00435700: DrawBridgeFloor
// 0x00435740: DrawBridgeCeiling
// 0x00435780: DrawBridgeCollision
// 0x004357B0: InitialiseLift
// 0x004357F0: LiftControl
// 0x004358D0: LiftFloorCeiling
// 0x00435A50: LiftFloor
// 0x00435A90: LiftCeiling
// 0x00435AD0: BridgeFlatFloor
// 0x00435AF0: BridgeFlatCeiling
// 0x00435B10: GetOffset
// 0x00435B50: BridgeTilt1Floor
// 0x00435B80: BridgeTilt1Ceiling
// 0x00435BC0: BridgeTilt2Floor
// 0x00435BF0: BridgeTilt2Ceiling
// 0x00435C30: CopterControl
// 0x00435D40: GeneralControl
// 0x00435E20: DetonatorControl
#endif // OBJECTS_H_INCLUDED
================================================
FILE: game/people.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/people.h"
#include "game/effects.h"
#include "game/items.h"
#include "game/sound.h"
#include "game/sphere.h"
#include "specific/game.h"
#include "global/vars.h"
#ifdef FEATURE_VIDEOFX_IMPROVED
extern DWORD AlphaBlendMode;
#endif // FEATURE_VIDEOFX_IMPROVED
__int16 __cdecl GunShot(int x, int y, int z, __int16 speed, __int16 rotY, __int16 roomNumber) {
#ifdef FEATURE_VIDEOFX_IMPROVED
if( AlphaBlendMode ) {
__int16 fx_id = CreateEffect(roomNumber);
if( fx_id >= 0) {
FX_INFO *fx = &Effects[fx_id];
fx->pos.x = x;
fx->pos.y = y;
fx->pos.z = z;
fx->room_number = roomNumber;
fx->counter = 4;
fx->speed = 0x400;
fx->frame_number = 0x200; // this is sprite scale
fx->fallspeed = 0;
fx->object_number = ID_GLOW;
fx->shade = 0x800;
// NOTE: Core's hacky way to store the sprite flags in the rotation fields
DWORD flags = GLOW_GUNSHOT_COLOR;
flags |= SPR_BLEND_ADD|SPR_TINT|SPR_SHADE|SPR_SCALE|SPR_SEMITRANS|SPR_ABS;
fx->pos.rotX=(UINT16)flags;
fx->pos.rotY=(UINT16)(flags >> 16);
}
}
#endif // FEATURE_VIDEOFX_IMPROVED
__int16 fx_id = CreateEffect(roomNumber);
if( fx_id >= 0 ) {
FX_INFO *fx = &Effects[fx_id];
fx->pos.x = x;
fx->pos.y = y;
fx->pos.z = z;
fx->room_number = roomNumber;
fx->pos.rotZ = 0;
fx->pos.rotX = 0;
fx->pos.rotY = rotY;
fx->counter = 3;
fx->frame_number = 0;
fx->object_number = ID_GUN_FLASH;
fx->shade = 0x1000;
}
return fx_id;
}
__int16 __cdecl GunHit(int x, int y, int z, __int16 speed, __int16 rotY, __int16 roomNumber) {
PHD_VECTOR pos = {0, 0, 0};
GetJointAbsPosition(LaraItem, &pos, GetRandomControl() * 25 / 0x7FFF);
#ifdef FEATURE_CHEAT
if( Lara.water_status == LWS_Cheat ) {
__int16 fxID = CreateEffect(roomNumber);
if( fxID >= 0 ) {
FX_INFO *fx = &Effects[fxID];
fx->pos.x = pos.x;
fx->pos.y = pos.y;
fx->pos.z = pos.z;
fx->counter = 4;
fx->object_number = ID_RICOCHET;
fx->pos.rotY = LaraItem->pos.rotY;
fx->speed = LaraItem->speed;
fx->frame_number = -3 * GetRandomDraw() / 0x8000;
}
PlaySoundEffect(10, &LaraItem->pos, 0);
} else {
DoBloodSplat(pos.x, pos.y, pos.z, LaraItem->speed, LaraItem->pos.rotY, LaraItem->roomNumber);
PlaySoundEffect(50, &LaraItem->pos, 0);
}
#else // FEATURE_CHEAT
DoBloodSplat(pos.x, pos.y, pos.z, LaraItem->speed, LaraItem->pos.rotY, LaraItem->roomNumber);
PlaySoundEffect(50, &LaraItem->pos, 0);
#endif // FEATURE_CHEAT
return GunShot(x, y, z, speed, rotY, roomNumber);
}
__int16 __cdecl GunMiss(int x, int y, int z, __int16 speed, __int16 rotY, __int16 roomNumber) {
GAME_VECTOR pos;
pos.x = LaraItem->pos.x + (GetRandomDraw() - 0x4000) * 0x200 / 0x7FFF;
pos.y = LaraItem->floor;
pos.z = LaraItem->pos.z + (GetRandomDraw() - 0x4000) * 0x200 / 0x7FFF;
pos.roomNumber = LaraItem->roomNumber;
Richochet(&pos);
return GunShot(x, y, z, speed, rotY, roomNumber);
}
/*
* Inject function
*/
void Inject_People() {
// INJECT(0x00435EB0, Targetable);
// INJECT(0x00435F40, ControlGlow);
// INJECT(0x00435F80, ControlGunShot);
INJECT(0x00435FD0, GunShot);
INJECT(0x00436040, GunHit);
INJECT(0x00436100, GunMiss);
// INJECT(0x004361B0, ShotLara);
// INJECT(0x00436380, InitialiseCult1);
// INJECT(0x004363D0, Cult1Control);
// INJECT(0x00436800, InitialiseCult3);
// INJECT(0x00436850, Cult3Control);
// INJECT(0x00436DC0, Worker1Control);
// INJECT(0x004371C0, Worker2Control);
// INJECT(0x00437620, BanditControl);
// INJECT(0x00437960, Bandit2Control);
// INJECT(0x00437DA0, WinstonControl);
}
================================================
FILE: game/people.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef PEOPLE_H_INCLUDED
#define PEOPLE_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x00435EB0: Targetable
// 0x00435F40: ControlGlow
// 0x00435F80: ControlGunShot
__int16 __cdecl GunShot(int x, int y, int z, __int16 speed, __int16 rotY, __int16 roomNumber); // 0x00435FD0
__int16 __cdecl GunHit(int x, int y, int z, __int16 speed, __int16 rotY, __int16 roomNumber); // 0x00436040
__int16 __cdecl GunMiss(int x, int y, int z, __int16 speed, __int16 rotY, __int16 roomNumber); // 0x00436100
// 0x004361B0: ShotLara
#define InitialiseCult1 ((void(__cdecl*)(__int16)) 0x00436380)
#define Cult1Control ((void(__cdecl*)(__int16)) 0x004363D0)
#define InitialiseCult3 ((void(__cdecl*)(__int16)) 0x00436800)
#define Cult3Control ((void(__cdecl*)(__int16)) 0x00436850)
#define Worker1Control ((void(__cdecl*)(__int16)) 0x00436DC0)
#define Worker2Control ((void(__cdecl*)(__int16)) 0x004371C0)
#define BanditControl ((void(__cdecl*)(__int16)) 0x00437620)
#define Bandit2Control ((void(__cdecl*)(__int16)) 0x00437960)
#define WinstonControl ((void(__cdecl*)(__int16)) 0x00437DA0)
#endif // PEOPLE_H_INCLUDED
================================================
FILE: game/pickup.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/pickup.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Pickup() {
// INJECT(0x00437F20, PickUpCollision);
// INJECT(0x004383A0, SwitchCollision);
// INJECT(0x004385B0, SwitchCollision2);
// INJECT(0x004386B0, DetonatorCollision);
// INJECT(0x004388F0, KeyHoleCollision);
// INJECT(0x00438B30, PuzzleHoleCollision);
// INJECT(0x00438DF0, SwitchControl);
// INJECT(0x00438E30, SwitchTrigger);
// INJECT(0x00438EF0, KeyTrigger);
// INJECT(0x00438F30, PickupTrigger);
// INJECT(0x00438F70, SecretControl);
}
================================================
FILE: game/pickup.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef PICKUP_H_INCLUDED
#define PICKUP_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define PickUpCollision ((void(__cdecl*)(__int16, ITEM_INFO *, COLL_INFO *)) 0x00437F20)
// 0x004383A0: SwitchCollision
// 0x004385B0: SwitchCollision2
// 0x004386B0: DetonatorCollision
// 0x004388F0: KeyHoleCollision
#define PuzzleHoleCollision ((void(__cdecl*)(__int16, ITEM_INFO *, COLL_INFO *)) 0x00438B30)
// 0x00438DF0: SwitchControl
// 0x00438E30: SwitchTrigger
// 0x00438EF0: KeyTrigger
// 0x00438F30: PickupTrigger
// 0x00438F70: SecretControl
#endif // PICKUP_H_INCLUDED
================================================
FILE: game/rat.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/rat.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Rat() {
// INJECT(0x00438FA0, MouseControl);
}
================================================
FILE: game/rat.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef RAT_H_INCLUDED
#define RAT_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define MouseControl ((void(__cdecl*)(__int16)) 0x00438FA0)
#endif // RAT_H_INCLUDED
================================================
FILE: game/savegame.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/savegame.h"
#include "game/control.h"
#include "game/dragon.h"
#include "game/invfunc.h"
#include "game/items.h"
#include "game/laramisc.h"
#include "game/moveblock.h"
#include "game/lot.h"
#include "game/pickup.h"
#include "game/skidoo.h"
#include "game/traps.h"
#include "specific/winmain.h"
#include "global/vars.h"
#define SGF_CREATURE (0x80)
void __cdecl InitialiseStartInfo() {
#if 0 // NOTE: this original check is removed, because it breaks game+ logic in case of any level selection
// skip initialise if bonus game started
if( SaveGame.bonusFlag )
return;
#endif
for( int i=0; i<24; ++i ) {
START_INFO *start = &SaveGame.start[i];
ModifyStartInfo(i);
start->available = 0; // make level not available
memset(&start->statistics, 0, sizeof(STATISTICS_INFO));
}
SaveGame.start[0].available = 1; // make assault available
SaveGame.start[1].available = 1; // make new game available
}
void __cdecl ModifyStartInfo(int levelIdx) {
START_INFO *start = &SaveGame.start[levelIdx];
start->has_pistols = 1; // Lara has pistols
start->gunType = LGT_Pistols; // current weapon is pistols
start->pistolAmmo = 1000; // infinite pistols ammo
switch( levelIdx ) {
case 0 : // Assault (Lara's Home)
start->available = 1; // make level available
start->has_pistols = 0; // Lara has no weapons
start->has_magnums = 0;
start->has_uzis = 0;
start->has_shotgun = 0;
start->has_m16 = 0;
start->has_grenade = 0;
start->has_harpoon = 0;
start->gunType = LGT_Unarmed; // Lara doesn't have weapons
start->gunStatus = LGS_Armless; // Lara has no weapons in hands
start->pistolAmmo = 0;
start->magnumAmmo = 0;
start->uziAmmo = 0;
start->shotgunAmmo = 0;
start->m16Ammo = 0;
start->grenadeAmmo = 0;
start->harpoonAmmo = 0;
start->flares = 0;
start->smallMedipacks = 0;
start->largeMedipacks = 0;
break;
case 1 : // Regular New Game
start->available = 1; // make level available
start->has_pistols = 1; // Lara has pistols and shotgun
start->has_magnums = 0;
start->has_uzis = 0;
start->has_shotgun = 1;
start->has_m16 = 0;
start->has_grenade = 0;
start->has_harpoon = 0;
start->gunStatus = LGS_Armless; // Lara has no weapons in hands
start->magnumAmmo = 0;
start->uziAmmo = 0;
start->shotgunAmmo = 12;
start->m16Ammo = 0;
start->grenadeAmmo = 0;
start->harpoonAmmo = 0;
start->flares = 2;
start->smallMedipacks = 1;
start->largeMedipacks = 1;
break;
// NOTE: there was no 'default' in the original game, so new game with level selection was broken
default : // New Game from any other level
if( SaveGame.start[levelIdx-1].statistics.timer ) break; // no reset if it's not a new game
start->available = 1; // make level available
start->has_pistols = 1; // Lara has just pistols
start->has_magnums = 0;
start->has_uzis = 0;
start->has_shotgun = 0;
start->has_m16 = 0;
start->has_grenade = 0;
start->has_harpoon = 0;
start->gunStatus = LGS_Armless; // Lara has no weapons in hands
start->magnumAmmo = 0;
start->uziAmmo = 0;
start->shotgunAmmo = 0;
start->m16Ammo = 0;
start->grenadeAmmo = 0;
start->harpoonAmmo = 0;
start->flares = 0;
start->smallMedipacks = 0;
start->largeMedipacks = 0;
break;
}
// Bonus game activated and level is not Assault
if( SaveGame.bonusFlag && levelIdx != 0 ) {
start->available = 1; // make level available
start->has_pistols = 1; // Lara has all weapons
start->has_magnums = 1;
start->has_uzis = 1;
start->has_shotgun = 1;
start->has_m16 = 1;
start->has_grenade = 1;
start->has_harpoon = 1;
start->gunType = LGT_Grenade; // current weapon is grenade launcher
start->uziAmmo = 10001;
start->magnumAmmo = 10001;
start->shotgunAmmo = 10001;
start->m16Ammo = 10001;
start->grenadeAmmo = 10001;
start->harpoonAmmo = 10001;
start->flares = 255;
}
}
void __cdecl CreateStartInfo(int levelID) {
START_INFO *start = &SaveGame.start[levelID];
start->available = 1;
start->pistolAmmo = 1000;
start->has_pistols = Inv_RequestItem(ID_PISTOL_ITEM) ? 1 : 0;
if( Inv_RequestItem(ID_MAGNUM_ITEM) ) {
start->magnumAmmo = Lara.magnum_ammo;;
start->has_magnums = 1;
} else {
start->magnumAmmo = 40 * Inv_RequestItem(ID_MAGNUM_AMMO_ITEM);
start->has_magnums = 0;
}
if( Inv_RequestItem(ID_UZI_ITEM) ) {
start->uziAmmo = Lara.uzi_ammo;
start->has_uzis = 1;
} else {
start->uziAmmo = 80 * Inv_RequestItem(ID_UZI_AMMO_ITEM);
start->has_uzis = 0;
}
if( Inv_RequestItem(ID_SHOTGUN_ITEM) ) {
start->shotgunAmmo = Lara.shotgun_ammo;
start->has_shotgun = 1;
} else {
start->shotgunAmmo = 12 * Inv_RequestItem(ID_SHOTGUN_AMMO_ITEM);
start->has_shotgun = 0;
}
if( Inv_RequestItem(ID_HARPOON_ITEM) ) {
start->harpoonAmmo = Lara.harpoon_ammo;
start->has_harpoon = 1;
} else {
start->harpoonAmmo = 3 * Inv_RequestItem(ID_HARPOON_AMMO_ITEM);
start->has_harpoon = 0;
}
if( Inv_RequestItem(ID_M16_ITEM) ) {
start->m16Ammo = Lara.m16_ammo;
start->has_m16 = 1;
} else {
start->m16Ammo = 40 * Inv_RequestItem(ID_M16_AMMO_ITEM);
start->has_m16 = 0;
}
if( Inv_RequestItem(ID_GRENADE_ITEM) ) {
start->grenadeAmmo = Lara.grenade_ammo;
start->has_grenade = 1;
} else {
start->grenadeAmmo = 2 * Inv_RequestItem(ID_GRENADE_AMMO_ITEM);
start->has_grenade = 0;
}
start->flares = Inv_RequestItem(ID_FLARE_ITEM);
start->smallMedipacks = Inv_RequestItem(ID_SMALL_MEDIPACK_ITEM);
start->largeMedipacks = Inv_RequestItem(ID_LARGE_MEDIPACK_ITEM);
start->gunType = ( Lara.gun_type == LGT_Flare ) ? Lara.last_gun_type : Lara.gun_type;
start->gunStatus = LGS_Armless;
}
void __cdecl CreateSaveGameInfo() {
ITEM_INFO *item = NULL;
SaveGame.currentLevel = CurrentLevel;
CreateStartInfo(CurrentLevel);
SaveGame.numPickup[0] = Inv_RequestItem(ID_PICKUP_ITEM1);
SaveGame.numPickup[1] = Inv_RequestItem(ID_PICKUP_ITEM2);
SaveGame.numPuzzle[0] = Inv_RequestItem(ID_PUZZLE_ITEM1);
SaveGame.numPuzzle[1] = Inv_RequestItem(ID_PUZZLE_ITEM2);
SaveGame.numPuzzle[2] = Inv_RequestItem(ID_PUZZLE_ITEM3);
SaveGame.numPuzzle[3] = Inv_RequestItem(ID_PUZZLE_ITEM4);
SaveGame.numKey[0] = Inv_RequestItem(ID_KEY_ITEM1);
SaveGame.numKey[1] = Inv_RequestItem(ID_KEY_ITEM2);
SaveGame.numKey[2] = Inv_RequestItem(ID_KEY_ITEM3);
SaveGame.numKey[3] = Inv_RequestItem(ID_KEY_ITEM4);
ResetSG();
memset(SaveGame.buffer, 0, sizeof(SaveGame.buffer));
WriteSG(&FlipStatus, sizeof(FlipStatus));
for( DWORD i=0; i> 8;
WriteSG(&flip, sizeof(flip));
}
WriteSG(CD_Flags, sizeof(CD_Flags));
for( DWORD i=0; iobjectID];
if( obj->save_position ) {
WriteSG(&item->pos, sizeof(item->pos));
WriteSG(&item->roomNumber, sizeof(item->roomNumber));
WriteSG(&item->speed, sizeof(item->speed));
WriteSG(&item->fallSpeed, sizeof(item->fallSpeed));
}
if( obj->save_anim ) {
WriteSG(&item->currentAnimState, sizeof(item->currentAnimState));
WriteSG(&item->goalAnimState, sizeof(item->goalAnimState));
WriteSG(&item->requiredAnimState, sizeof(item->requiredAnimState));
WriteSG(&item->animNumber, sizeof(item->animNumber));
WriteSG(&item->frameNumber, sizeof(item->frameNumber));
}
if( obj->save_hitpoints ) {
WriteSG(&item->hitPoints, sizeof(item->hitPoints));
}
if( obj->save_flags ) {
__int16 flags = item->flags;
flags |= item->active | (item->status << 1) | (item->gravity << 3) | (item->collidable << 4);
if( obj->intelligent && item->data != NULL ) {
flags |= SGF_CREATURE;
}
WriteSG(&flags, sizeof(flags));
if( obj->intelligent ) {
WriteSG(&item->carriedItem, sizeof(item->carriedItem));
}
WriteSG(&item->timer, sizeof(item->timer));
if( CHK_ANY(flags, SGF_CREATURE) ) {
CREATURE_INFO *creature = (CREATURE_INFO *)item->data;
WriteSG(&creature->head_rotation, sizeof(creature->head_rotation));
WriteSG(&creature->neck_rotation, sizeof(creature->neck_rotation));
WriteSG(&creature->maximum_turn, sizeof(creature->maximum_turn));
WriteSG(&creature->flags, sizeof(creature->flags));
WriteSG(&creature->mood, sizeof(creature->mood));
}
}
switch( item->objectID ) {
case ID_BOAT:
WriteSG(item->data, sizeof(BOAT_INFO));
break;
case ID_SKIDOO_FAST:
WriteSG(item->data, sizeof(SKIDOO_INFO));
break;
case ID_LIFT:
WriteSG(item->data, sizeof(int) * 2);
break;
default:
break;
}
}
for( DWORD i=0; i= 0 ) {
item = &Items[Lara.weapon_item];
WriteSG(&item->objectID, sizeof(item->objectID));
WriteSG(&item->animNumber, sizeof(item->animNumber));
WriteSG(&item->frameNumber, sizeof(item->frameNumber));
WriteSG(&item->currentAnimState, sizeof(item->currentAnimState));
WriteSG(&item->goalAnimState, sizeof(item->goalAnimState));
}
WriteSG(&FlipEffect, sizeof(FlipEffect));
WriteSG(&FlipTimer, sizeof(FlipTimer));
WriteSG(&IsMonkAngry, sizeof(IsMonkAngry));
int numFlares = 0;
for( int i=LevelItemCount; i<256; ++i ) {
item = &Items[i];
if( item->active && item->objectID == ID_FLARE_ITEM ) {
++numFlares;
}
}
WriteSG(&numFlares, sizeof(numFlares));
for( int i=LevelItemCount; i<256; ++i ) {
item = &Items[i];
if( item->active && item->objectID == ID_FLARE_ITEM ) {
WriteSG(&item->pos, sizeof(item->pos));
WriteSG(&item->roomNumber, sizeof(item->roomNumber));
WriteSG(&item->speed, sizeof(item->speed));
WriteSG(&item->fallSpeed, sizeof(item->fallSpeed));
int flareAge = (int)item->data;
WriteSG(&flareAge, sizeof(flareAge));
}
}
}
void __cdecl ExtractSaveGameInfo() {
ITEM_INFO *item = NULL;
InitialiseLaraInventory(CurrentLevel);
for( int i=0; i<2; ++i ) {
GAME_OBJECT_ID id[] = {ID_PICKUP_ITEM1, ID_PICKUP_ITEM2};
for( int j=0; jobjectID];
if( obj->control == MovableBlock ) {
AlterFloorHeight(item, 0x400);
}
if( obj->save_position ) {
__int16 roomNumber;
ReadSG(&item->pos, sizeof(item->pos));
ReadSG(&roomNumber, sizeof(roomNumber));
ReadSG(&item->speed, sizeof(item->speed));
ReadSG(&item->fallSpeed, sizeof(item->fallSpeed));
if( item->roomNumber != roomNumber ) {
ItemNewRoom(i, roomNumber);
}
if( obj->shadowSize ) {
FLOOR_INFO *floor = GetFloor(item->pos.x, item->pos.y, item->pos.z, &roomNumber);
item->floor = GetHeight(floor, item->pos.x, item->pos.y, item->pos.z);
}
}
if( obj->save_anim ) {
ReadSG(&item->currentAnimState, sizeof(item->currentAnimState));
ReadSG(&item->goalAnimState, sizeof(item->goalAnimState));
ReadSG(&item->requiredAnimState, sizeof(item->requiredAnimState));
ReadSG(&item->animNumber, sizeof(item->animNumber));
ReadSG(&item->frameNumber, sizeof(item->frameNumber));
}
if( obj->save_hitpoints ) {
ReadSG(&item->hitPoints, sizeof(item->hitPoints));
}
if( obj->save_flags ) {
ReadSG(&item->flags, sizeof(item->flags));
if( obj->intelligent ) {
ReadSG(&item->carriedItem, sizeof(item->carriedItem));
}
ReadSG(&item->timer, sizeof(item->timer));
if( CHK_ANY(item->flags, IFL_CLEARBODY) ) {
KillItem(i);
item->status = ITEM_DISABLED;
} else {
if( CHK_ANY(item->flags, 0x01) && !item->active ) {
AddActiveItem(i);
}
item->status = (item->flags >> 1) & 3;
if( CHK_ANY(item->flags, 0x08) ) {
item->gravity = 1;
}
if( !CHK_ANY(item->flags, 0x10) ) {
item->collidable = 0;
}
}
if( CHK_ANY(item->flags, SGF_CREATURE) ) {
EnableBaddieAI(i, 1);
CREATURE_INFO *creature = (CREATURE_INFO *)item->data;
if( creature != NULL ) {
ReadSG(&creature->head_rotation, sizeof(creature->head_rotation));
ReadSG(&creature->neck_rotation, sizeof(creature->neck_rotation));
ReadSG(&creature->maximum_turn, sizeof(creature->maximum_turn));
ReadSG(&creature->flags, sizeof(creature->flags));
ReadSG(&creature->mood, sizeof(creature->mood));
}
else {
SG_Point += sizeof(__int16) * 4 + sizeof(MOOD_TYPE);
}
} else if( obj->intelligent ) {
item->data = NULL;
if( item->clear_body && item->hitPoints <= 0 && !CHK_ANY(item->flags, IFL_CLEARBODY) ) {
item->nextActive = PrevItemActive;
PrevItemActive = i;
}
}
item->flags &= ~0xFF;
if( obj->collision == PuzzleHoleCollision && (item->status == ITEM_ACTIVE || item->status == ITEM_DISABLED) ) {
item->objectID += ID_PUZZLE_DONE1 - ID_PUZZLE_HOLE1;
}
if( obj->collision == PickUpCollision && item->status == ITEM_DISABLED ) {
RemoveDrawnItem(i);
}
if( (item->objectID == ID_WINDOW1 || item->objectID == ID_WINDOW2) && CHK_ANY(item->flags, IFL_INVISIBLE) ) {
item->meshBits = 0x100;
}
if( item->objectID == ID_MINE && CHK_ANY(item->flags, IFL_INVISIBLE) ) {
item->meshBits = 1;
}
}
if( obj->control == MovableBlock && item->status == ITEM_INACTIVE ) {
AlterFloorHeight(item, -0x400u);
}
switch( item->objectID ) {
case ID_BOAT:
ReadSG(item->data, sizeof(BOAT_INFO));
break;
case ID_SKIDOO_FAST:
ReadSG(item->data, sizeof(SKIDOO_INFO));
break;
case ID_LIFT:
ReadSG(item->data, sizeof(int) * 2);
break;
default:
break;
}
if( item->objectID == ID_SKIDMAN && item->status == ITEM_DISABLED ) {
Items[(int)item->data].objectID = ID_SKIDOO_FAST;
InitialiseSkidoo((int)item->data);
}
if( item->objectID == ID_DRAGON_FRONT && item->status == ITEM_DISABLED ) {
item->pos.y -= 1010;
DragonBones(i);
item->pos.y += 1010;
}
}
ReadSG(&Lara, sizeof(Lara));
Lara.creature = NULL;
Lara.spaz_effect = NULL;
Lara.target = NULL;
for( DWORD i=0; i= 0 ) {
Lara.weapon_item = CreateItem();
item = &Items[Lara.weapon_item];
ReadSG(&item->objectID, sizeof(item->objectID));
ReadSG(&item->animNumber, sizeof(item->animNumber));
ReadSG(&item->frameNumber, sizeof(item->frameNumber));
ReadSG(&item->currentAnimState, sizeof(item->currentAnimState));
ReadSG(&item->goalAnimState, sizeof(item->goalAnimState));
item->status = ITEM_ACTIVE;
item->roomNumber = 0xFF;
}
if( Lara.burn ) {
Lara.burn = 0;
LaraBurn();
}
ReadSG(&FlipEffect, sizeof(FlipEffect));
ReadSG(&FlipTimer, sizeof(FlipTimer));
ReadSG(&IsMonkAngry, sizeof(IsMonkAngry));
int numFlares = 0;
ReadSG(&numFlares, sizeof(numFlares));
for( int i=0; iobjectID = ID_FLARE_ITEM;
ReadSG(&item->pos, sizeof(item->pos));
ReadSG(&item->roomNumber, sizeof(item->roomNumber));
ReadSG(&item->speed, sizeof(item->speed));
ReadSG(&item->fallSpeed, sizeof(item->fallSpeed));
InitialiseItem(itemID);
AddActiveItem(itemID);
int flareAge;
ReadSG(&flareAge, sizeof(flareAge));
item->data = (LPVOID)flareAge;
}
}
void __cdecl ResetSG() {
SG_Count = 0;
SG_Point = SaveGame.buffer;
}
void __cdecl WriteSG(void *ptr, int len) {
SG_Count += len;
if( SG_Count >= sizeof(SaveGame.buffer) ) {
S_ExitSystem("FATAL: Savegame is too big to fit in buffer");
}
memcpy(SG_Point, ptr, len);
SG_Point += len;
}
void __cdecl ReadSG(void *ptr, int len) {
SG_Count += len;
memcpy(ptr, SG_Point, len);
SG_Point += len;
}
/*
* Inject function
*/
void Inject_SaveGame() {
INJECT(0x00439190, InitialiseStartInfo);
INJECT(0x00439200, ModifyStartInfo);
INJECT(0x004392E0, CreateStartInfo);
INJECT(0x004394F0, CreateSaveGameInfo);
INJECT(0x00439A20, ExtractSaveGameInfo);
INJECT(0x0043A280, ResetSG);
INJECT(0x0043A2A0, WriteSG);
INJECT(0x0043A2F0, ReadSG);
}
================================================
FILE: game/savegame.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef SAVEGAME_H_INCLUDED
#define SAVEGAME_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl InitialiseStartInfo(); // 0x00439190
void __cdecl ModifyStartInfo(int levelIdx); // 0x00439200
void __cdecl CreateStartInfo(int levelID); // 0x004392E0
void __cdecl CreateSaveGameInfo(); // 0x004394F0
void __cdecl ExtractSaveGameInfo(); // 0x00439A20
void __cdecl ResetSG(); // 0x0043A280
void __cdecl WriteSG(void *ptr, int len); // 0x0043A2A0
void __cdecl ReadSG(void *ptr, int len); // 0x0043A2F0
#endif // SAVEGAME_H_INCLUDED
================================================
FILE: game/setup.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/setup.h"
#include "game/bear.h"
#include "game/bird.h"
#include "game/collide.h"
#include "game/diver.h"
#include "game/dog.h"
#include "game/dragon.h"
#include "game/draw.h"
#include "game/eel.h"
#include "game/enemies.h"
#include "game/hair.h"
#include "game/laramisc.h"
#include "game/moveblock.h"
#include "game/people.h"
#include "game/rat.h"
#include "game/shark.h"
#include "game/skidoo.h"
#include "game/spider.h"
#include "game/wolf.h"
#include "game/yeti.h"
#include "specific/winmain.h"
#include "global/vars.h"
#ifdef FEATURE_GOLD
extern bool IsGold();
#endif
void __cdecl InitialiseLevelFlags() {
memset(&SaveGame.statistics, 0, sizeof(STATISTICS_INFO));
}
void __cdecl InitialiseObjects() {
for( int i = 0; i < ID_NUMBER_OBJECTS; ++i ) {
Objects[i].intelligent = 0;
Objects[i].save_position = 0;
Objects[i].save_hitpoints = 0;
Objects[i].save_flags = 0;
Objects[i].save_anim = 0;
Objects[i].water_creature = 0;
Objects[i].initialise = NULL;
Objects[i].collision = NULL;
Objects[i].control = NULL;
Objects[i].drawRoutine = DrawAnimatingItem;
Objects[i].ceiling = NULL;
Objects[i].floor = NULL;
Objects[i].pivotLength = 0;
Objects[i].radius = 10;
Objects[i].shadowSize = 0;
Objects[i].hitPoints = HP_DONT_TARGET;
}
BaddyObjects();
TrapObjects();
ObjectObjects();
InitialiseHair();
}
void __cdecl BaddyObjects() {
OBJECT_INFO *obj;
// Lara object is mandatory
obj = &Objects[ID_LARA];
obj->initialise = InitialiseLaraLoad;
obj->shadowSize = 160;
obj->hitPoints = 1000;
obj->drawRoutine = DrawDummyItem;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->control = ControlLaraExtra;
// Other objects are optional
obj = &Objects[ID_DOG];
if( obj->loaded ) {
obj->control = DogControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 10;
obj->pivotLength = 300;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->radius = 341;
AnimBones[obj->boneIndex + 19*4] |= 0x08;
}
obj = &Objects[ID_MOUSE];
if( obj->loaded ) {
obj->control = MouseControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 4;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->pivotLength = 50;
obj->radius = 102;
AnimBones[obj->boneIndex + 3*4] |= 0x08;
}
obj = &Objects[ID_CULT1];
if( obj->loaded ) {
obj->initialise = InitialiseCult1;
obj->control = Cult1Control;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->hitPoints = 25;
obj->pivotLength = 50;
obj->radius = 102;
AnimBones[obj->boneIndex + 0*4] |= 0x08;
}
obj = &Objects[ID_CULT1A];
if( obj->loaded ) {
if( !Objects[ID_CULT1].loaded ) {
S_ExitSystem("FATAL: CULT1A requires CULT1"); // NOTE: there was a typo: CULT1B instead of CULT1A
}
obj->frameBase = Objects[ID_CULT1].frameBase;
obj->animIndex = Objects[ID_CULT1].animIndex;
obj->initialise = InitialiseCult1;
obj->control = Cult1Control;
obj->collision = CreatureCollision;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->shadowSize = 128;
obj->hitPoints = 25;
obj->pivotLength = 50;
obj->radius = 102;
AnimBones[obj->boneIndex + 0*4] |= 0x08;
}
obj = &Objects[ID_CULT1B];
if( obj->loaded ) {
if( !Objects[ID_CULT1].loaded ) {
S_ExitSystem("FATAL: CULT1B requires CULT1");
}
obj->frameBase = Objects[ID_CULT1].frameBase;
obj->animIndex = Objects[ID_CULT1].animIndex;
obj->initialise = InitialiseCult1;
obj->control = Cult1Control;
obj->collision = CreatureCollision;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->shadowSize = 128;
obj->hitPoints = 25;
obj->pivotLength = 50;
obj->radius = 102;
AnimBones[obj->boneIndex + 0*4] |= 0x08;
}
obj = &Objects[ID_CULT2];
if( obj->loaded ) {
obj->control = Cult2Control;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 60;
obj->pivotLength = 50;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->radius = 102;
AnimBones[obj->boneIndex + 0*4] |= 0x08;
AnimBones[obj->boneIndex + 8*4] |= 0x08;
}
obj = &Objects[ID_SHARK];
if( obj->loaded ) {
obj->control = SharkControl;
obj->drawRoutine = DrawUnclippedItem;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 30;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->water_creature = 1;
obj->pivotLength = 200;
obj->radius = 341;
AnimBones[obj->boneIndex + 9*4] |= 0x08;
}
obj = &Objects[ID_TIGER];
if( obj->loaded ) {
obj->control = TigerControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 20;
obj->pivotLength = 200;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->radius = 341;
AnimBones[obj->boneIndex + 21*4] |= 0x08;
}
obj = &Objects[ID_BARRACUDA];
if( obj->loaded ) {
obj->control = BaracuddaControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 12;
obj->pivotLength = 200;
obj->radius = 204;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->water_creature = 1;
AnimBones[obj->boneIndex + 6*4] |= 0x08;
}
obj = &Objects[ID_SPIDER_or_WOLF];
if( obj->loaded ) {
#ifdef FEATURE_GOLD
obj->initialise = IsGold() ? InitialiseWolf : NULL;
obj->control = IsGold() ? WolfControl : SpiderControl;
obj->hitPoints = IsGold() ? 10 : 5;
obj->radius = IsGold() ? 341 : 102;
#else
obj->control = SpiderControl;
obj->hitPoints = 5;
obj->radius = 102;
#endif
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
}
obj = &Objects[ID_BIG_SPIDER_or_BEAR];
if( obj->loaded ) {
#ifdef FEATURE_GOLD
obj->control = IsGold() ? BearControl : BigSpiderControl;
obj->hitPoints = IsGold() ? 30 : 40;
#else
obj->control = BigSpiderControl;
obj->hitPoints = 40;
#endif
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->radius = 341;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
}
obj = &Objects[ID_YETI];
if( obj->loaded ) {
obj->control = YetiControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 30;
obj->radius = 128;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->pivotLength = 100;
AnimBones[obj->boneIndex + 6*4] |= 0x08;
AnimBones[obj->boneIndex + 14*4] |= 0x08;
}
obj = &Objects[ID_JELLY];
if( obj->loaded ) {
obj->control = JellyControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 10;
obj->radius = 102;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->water_creature = 1;
}
obj = &Objects[ID_DIVER];
if( obj->loaded ) {
obj->control = DiverControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 20;
obj->radius = 341;
obj->pivotLength = 50;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->water_creature = 1;
AnimBones[obj->boneIndex + 10*4] |= 0x08;
AnimBones[obj->boneIndex + 14*4] |= 0x10;
}
obj = &Objects[ID_WORKER1];
if( obj->loaded ) {
obj->control = Worker1Control;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 25;
obj->radius = 102;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->pivotLength = 0;
AnimBones[obj->boneIndex + 4*4] |= 0x08;
AnimBones[obj->boneIndex + 13*4] |= 0x08;
}
obj = &Objects[ID_WORKER2];
if( obj->loaded ) {
obj->control = Worker2Control;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 20;
obj->radius = 102;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->pivotLength = 0;
AnimBones[obj->boneIndex + 4*4] |= 0x08;
AnimBones[obj->boneIndex + 13*4] |= 0x08;
}
obj = &Objects[ID_WORKER3];
if( obj->loaded ) {
obj->control = Worker3Control;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 27;
obj->radius = 102;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->pivotLength = 0;
AnimBones[obj->boneIndex + 0*4] |= 0x08;
AnimBones[obj->boneIndex + 4*4] |= 0x08;
}
obj = &Objects[ID_WORKER4];
if( obj->loaded ) {
obj->hitPoints = 27;
obj->control = Worker3Control;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->radius = 102;
obj->pivotLength = 0;
AnimBones[obj->boneIndex + 0*4] |= 0x08;
AnimBones[obj->boneIndex + 4*4] |= 0x08;
}
obj = &Objects[ID_WORKER5];
if( obj->loaded ) {
obj->control = Worker2Control;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 20;
obj->radius = 102;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->pivotLength = 0;
AnimBones[obj->boneIndex + 4*4] |= 0x08;
AnimBones[obj->boneIndex + 13*4] |= 0x08;
}
obj = &Objects[ID_CULT3];
if( obj->loaded ) {
obj->initialise = InitialiseCult3;
obj->control = Cult3Control;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 150;
obj->radius = 102;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->pivotLength = 0;
}
obj = &Objects[ID_MONK1];
if( obj->loaded ) {
obj->control = MonkControl;
obj->collision = CreatureCollision;
#ifdef FEATURE_GOLD
obj->shadowSize = IsGold() ? 0 : 128;
#else
obj->shadowSize = 128;
#endif
obj->hitPoints = 30;
obj->radius = 102;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->pivotLength = 0;
AnimBones[obj->boneIndex + 6*4] |= 0x08;
}
obj = &Objects[ID_MONK2];
if( obj->loaded ) {
obj->control = MonkControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 30;
obj->radius = 102;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
AnimBones[obj->boneIndex + 6*4] |= 0x08;
}
obj = &Objects[ID_EAGLE];
if( obj->loaded ) {
obj->initialise = InitialiseEagle;
obj->control = EagleControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 20;
obj->radius = 204;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->pivotLength = 0;
}
obj = &Objects[ID_CROW];
if( obj->loaded ) {
obj->initialise = InitialiseEagle;
obj->control = EagleControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 15;
obj->radius = 204;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->pivotLength = 0;
}
obj = &Objects[ID_BIG_EEL];
if( obj->loaded ) {
obj->control = BigEelControl;
obj->collision = CreatureCollision;
obj->hitPoints = 20;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->water_creature = 1;
}
obj = &Objects[ID_EEL];
if( obj->loaded ) {
obj->control = EelControl;
obj->collision = CreatureCollision;
obj->hitPoints = 5;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->water_creature = 1;
}
obj = &Objects[ID_BANDIT1];
if( obj->loaded ) {
obj->control = BanditControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 45;
obj->radius = 102;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->pivotLength = 0;
AnimBones[obj->boneIndex + 6*4] |= 0x08;
AnimBones[obj->boneIndex + 8*4] |= 0x08;
}
obj = &Objects[ID_BANDIT2];
if( obj->loaded ) {
obj->control = Bandit2Control;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->hitPoints = 50;
obj->radius = 102;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->pivotLength = 0;
AnimBones[obj->boneIndex + 6*4] |= 0x08;
AnimBones[obj->boneIndex + 8*4] |= 0x08;
}
obj = &Objects[ID_BANDIT2B];
if( obj->loaded ) {
if( !Objects[ID_BANDIT2].loaded ) {
S_ExitSystem("FATAL: BANDIT2B requires BANDIT2");
}
obj->frameBase = Objects[ID_BANDIT2].frameBase;
obj->animIndex = Objects[ID_BANDIT2].animIndex;
obj->control = Bandit2Control;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->hitPoints = 50;
obj->radius = 102;
obj->pivotLength = 0;
AnimBones[obj->boneIndex + 6*4] |= 0x08;
AnimBones[obj->boneIndex + 8*4] |= 0x08;
}
obj = &Objects[ID_SKIDOO_ARMED];
if( obj->loaded ) {
obj->drawRoutine = DrawSkidoo;
obj->collision = SkidmanCollision;
obj->shadowSize = 128;
obj->hitPoints = 100;
obj->radius = 341;
obj->pivotLength = 0;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
}
obj = &Objects[ID_SKIDMAN];
if( obj->loaded ) {
obj->initialise = InitialiseSkidman;
obj->control = SkidManControl;
obj->hitPoints = 1;
obj->save_position = 1;
obj->save_flags = 1;
obj->save_anim = 1;
}
obj = &Objects[ID_XIAN_LORD];
if( obj->loaded ) {
if( !Objects[ID_CHINESE2].loaded ) {
S_ExitSystem("FATAL: XianLord requires CHINESE2 (statue)");
}
obj->initialise = InitialiseXianLord;
obj->drawRoutine = DrawXianLord;
obj->control = XianLordControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->hitPoints = 100;
obj->radius = 204;
obj->pivotLength = 0;
AnimBones[obj->boneIndex + 6*4] |= 0x08;
AnimBones[obj->boneIndex + 12*4] |= 0x08;
}
obj = &Objects[ID_WARRIOR];
if( obj->loaded ) {
if( !Objects[ID_CHINESE4].loaded ) {
S_ExitSystem("FATAL: Warrior requires CHINESE4 (statue)");
}
obj->drawRoutine = DrawXianLord;
obj->initialise = InitialiseXianLord;
obj->control = WarriorControl;
obj->collision = CreatureCollision;
obj->shadowSize = 128;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->hitPoints = 80;
obj->radius = 204;
obj->pivotLength = 0;
AnimBones[obj->boneIndex + 6*4] |= 0x08;
AnimBones[obj->boneIndex + 16*4] |= 0x08;
}
obj = &Objects[ID_DRAGON_FRONT];
if( obj->loaded ) {
if( !Objects[ID_DRAGON_BACK].loaded ) {
S_ExitSystem("FATAL: Dragon front needs back");
}
obj->hitPoints = 300;
obj->pivotLength = 300;
obj->control = DragonControl;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->collision = DragonCollision;
obj->radius = 341;
AnimBones[obj->boneIndex + 10*4] |= 0x10;
}
obj = &Objects[ID_DRAGON_BACK];
if( obj->loaded ) {
obj->control = DragonControl;
obj->collision = DragonCollision;
obj->radius = 341;
obj->save_position = 1;
obj->save_flags = 1;
obj->save_anim = 1;
}
obj = &Objects[ID_BARTOLI];
if( obj->loaded ) {
obj->initialise = InitialiseBartoli;
obj->control = BartoliControl;
obj->save_flags = 1;
obj->save_anim = 1;
}
obj = &Objects[ID_GIANT_YETI];
if( obj->loaded ) {
obj->control = GiantYetiControl;
obj->collision = CreatureCollision;
obj->hitPoints = 200;
obj->radius = 341;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
AnimBones[obj->boneIndex + 14*4] |= 0x08;
}
obj = &Objects[ID_DINO];
if( obj->loaded ) {
obj->control = DinoControl;
obj->collision = CreatureCollision;
obj->hitPoints = 100;
obj->shadowSize = 64;
obj->pivotLength = 1800;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_hitpoints = 1;
obj->save_flags = 1;
obj->save_anim = 1;
obj->radius = 341;
AnimBones[obj->boneIndex + 10*4] |= 0x08;
AnimBones[obj->boneIndex + 11*4] |= 0x08;
}
obj = &Objects[ID_WINSTON];
if( obj->loaded ) {
obj->control = WinstonControl;
obj->collision = ObjectCollision;
obj->hitPoints = HP_DONT_TARGET;
obj->shadowSize = 64;
obj->radius = 102;
obj->intelligent = 1;
obj->save_position = 1;
obj->save_flags = 1;
obj->save_anim = 1;
}
}
/*
* Inject function
*/
void Inject_Setup() {
// INJECT(0x0043A330, InitialiseLevel);
// INJECT(0x0043A490, InitialiseGameFlags);
INJECT(0x0043A500, InitialiseLevelFlags);
INJECT(0x0043A530, BaddyObjects);
// INJECT(0x0043B570, TrapObjects);
// INJECT(0x0043BB70, ObjectObjects);
INJECT(0x0043C7C0, InitialiseObjects);
// INJECT(0x0043C830, GetCarriedItems);
}
================================================
FILE: game/setup.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef SETUP_H_INCLUDED
#define SETUP_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define InitialiseLevel ((BOOL(__cdecl*)(int,int)) 0x0043A330)
// 0x0043A490: InitialiseGameFlags
void __cdecl InitialiseLevelFlags(); // 0x0043A500
void __cdecl BaddyObjects(); // 0x0043A530
#define TrapObjects ((void(__cdecl*)(void)) 0x0043B570)
#define ObjectObjects ((void(__cdecl*)(void)) 0x0043BB70)
void __cdecl InitialiseObjects(); // 0x0043C7C0
// 0x0043C830: GetCarriedItems
#endif // SETUP_H_INCLUDED
================================================
FILE: game/shark.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/shark.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Shark() {
// INJECT(0x0043C900, JellyControl);
// INJECT(0x0043CA20, BaracuddaControl);
// INJECT(0x0043CC50, SharkControl);
}
================================================
FILE: game/shark.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef SHARK_H_INCLUDED
#define SHARK_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define JellyControl ((void(__cdecl*)(__int16)) 0x0043C900)
#define BaracuddaControl ((void(__cdecl*)(__int16)) 0x0043CA20)
#define SharkControl ((void(__cdecl*)(__int16)) 0x0043CC50)
#endif // SHARK_H_INCLUDED
================================================
FILE: game/skidoo.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/skidoo.h"
#include "3dsystem/3d_gen.h"
#include "3dsystem/phd_math.h"
#include "game/box.h"
#include "game/draw.h"
#include "game/items.h"
#include "game/larafire.h"
#include "game/missile.h"
#include "game/people.h"
#include "game/sound.h"
#include "specific/game.h"
#include "specific/output.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/joy_output.h"
#endif // FEATURE_INPUT_IMPROVED
static BITE_INFO SkidooLeftGun = {219, -71, 550, 0};
static BITE_INFO SkidooRightGun = {-235, -71, 550, 0};
void __cdecl DoSnowEffect(ITEM_INFO *item) {
__int16 fxID;
FX_INFO *fx;
int displacement;
fxID = CreateEffect(item->roomNumber);
if (fxID != -1) {
fx = &Effects[fxID];
displacement = 260 * (GetRandomDraw() - 16384) >> 14;
fx->pos.x = item->pos.x - ((500 * phd_sin(item->pos.rotY) + displacement * phd_cos(item->pos.rotY)) >> W2V_SHIFT);
fx->pos.y = item->pos.y + (500 * phd_sin(item->pos.rotX) >> W2V_SHIFT);
fx->pos.z = item->pos.z - ((500 * phd_cos(item->pos.rotY) - displacement * phd_sin(item->pos.rotY)) >> W2V_SHIFT);
fx->room_number = item->roomNumber;
fx->frame_number = 0;
fx->object_number = ID_SNOW_SPRITE;
fx->speed = 0;
if (item->speed < 64) {
fx->fallspeed = (ABS(item->speed) - 64) * GetRandomDraw() >> 15;
} else {
fx->fallspeed = 0;
}
PhdMatrixPtr->_23 = 0;
S_CalculateLight(fx->pos.x, fx->pos.y, fx->pos.z, fx->room_number);
fx->shade = LsAdder - 512;
CLAMPL(fx->shade, 0);
}
}
void __cdecl SkidooExplode(ITEM_INFO *item) {
__int16 fxID;
FX_INFO *fx;
fxID = CreateEffect(item->roomNumber);
if (fxID != -1) {
fx = &Effects[fxID];
fx->pos.x = item->pos.x;
fx->pos.y = item->pos.y;
fx->pos.z = item->pos.z;
fx->speed = 0;
fx->frame_number = 0;
fx->counter = 0;
fx->object_number = ID_EXPLOSION;
}
ExplodingDeath(Lara.skidoo, ~3, 0);
PlaySoundEffect(105, NULL, 0);
Lara.skidoo = -1;
#ifdef FEATURE_INPUT_IMPROVED
JoyRumbleExplode(LaraItem->pos.x, LaraItem->pos.y, LaraItem->pos.z, 0x1400, true);
#endif // FEATURE_INPUT_IMPROVED
}
void __cdecl SkidooGuns() {
WEAPON_INFO *weapon = &Weapons[LGT_Skidoo];
LaraGetNewTarget(weapon);
AimWeapon(weapon, &Lara.right_arm);
if( CHK_ANY(InputStatus, IN_ACTION) ) {
__int16 angles[2];
angles[0] = Lara.right_arm.y_rot + LaraItem->pos.rotY;
angles[1] = Lara.right_arm.x_rot;
if( FireWeapon(LGT_Skidoo, Lara.target, LaraItem, angles) ) {
Lara.right_arm.flash_gun = weapon->flashTime;
PlaySoundEffect(weapon->sampleNum, &LaraItem->pos, 0);
int x = LaraItem->pos.x + (phd_sin(LaraItem->pos.rotY) >> (W2V_SHIFT-10));
int y = LaraItem->pos.y - 0x200;
int z = LaraItem->pos.z + (phd_cos(LaraItem->pos.rotY) >> (W2V_SHIFT-10));
AddDynamicLight(x, y, z, 12, 11);
CreatureEffect(&Items[Lara.skidoo], &SkidooLeftGun, GunShot);
CreatureEffect(&Items[Lara.skidoo], &SkidooRightGun, GunShot);
#ifdef FEATURE_INPUT_IMPROVED
JoyVibrate(0x400, 0x400, 2, 0x80, 4, false);
#endif // FEATURE_INPUT_IMPROVED
}
}
}
void __cdecl DrawSkidoo(ITEM_INFO *item) {
__int16 *frames[2];
int rate = 0;
UINT16 flags = 0;
OBJECT_INFO *obj;
int frac = GetFrames(item, frames, &rate);
if( item->data ) {
flags = *(UINT16 *)item->data;
}
if( CHK_ANY(flags, 4) ) {
obj = &Objects[ID_SKIDOO_ARMED];
} else {
obj = &Objects[item->objectID];
}
phd_PushMatrix();
phd_TranslateAbs(item->pos.x, item->pos.y, item->pos.z);
phd_RotYXZ(item->pos.rotY, item->pos.rotX, item->pos.rotZ);
int clip = S_GetObjectBounds(frames[0]);
if( clip ) {
CalculateObjectLighting(item, frames[0]);
__int16 **track = 0;
__int16 **meshPtr = &MeshPtr[obj->meshIndex];
int *bonePtr = &AnimBones[obj->boneIndex];
if( (flags & 3) == 1 ) {
track = &MeshPtr[Objects[ID_SKIDOO_LARA].meshIndex + 1];
} else if( (flags & 3) == 2 ) {
track = &MeshPtr[Objects[ID_SKIDOO_LARA].meshIndex + 7];
}
if( frac ) {
UINT16 *rot1 = (UINT16 *)&frames[0][9];
UINT16 *rot2 = (UINT16 *)&frames[1][9];
InitInterpolate(frac, rate);
phd_TranslateRel_ID(frames[0][6], frames[0][7], frames[0][8], frames[1][6], frames[1][7], frames[1][8]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(CHK_ANY(flags, 4) ? (int)ID_SKIDOO_ARMED : item->objectID, 0);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons_I(meshPtr[0], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
for( int i = 1; i < obj->nMeshes; ++i ) {
DWORD state = *bonePtr;
if( CHK_ANY(state, 1) ) {
phd_PopMatrix_I();
}
if( CHK_ANY(state, 2) ) {
phd_PushMatrix_I();
}
phd_TranslateRel_I(bonePtr[1], bonePtr[2], bonePtr[3]);
phd_RotYXZsuperpack_I(&rot1, &rot2, 0);
if( track ) {
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(ID_SKIDOO_LARA, (flags & 3) == 1 ? 1 : 7);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons_I(*track, clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
track = NULL;
} else {
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(CHK_ANY(flags, 4) ? (int)ID_SKIDOO_ARMED : item->objectID, i);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons_I(meshPtr[i], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
}
bonePtr += 4;
}
} else {
UINT16 *rot = (UINT16 *)&frames[0][9];
phd_TranslateRel(frames[0][6], frames[0][7], frames[0][8]);
phd_RotYXZsuperpack(&rot, 0);
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(CHK_ANY(flags, 4) ? (int)ID_SKIDOO_ARMED : item->objectID, 0);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons(meshPtr[0], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
for( int i = 1; i < obj->nMeshes; ++i ) {
DWORD state = *bonePtr;
if( CHK_ANY(state, 1) ) {
phd_PopMatrix();
}
if( CHK_ANY(state, 2) ) {
phd_PushMatrix();
}
phd_TranslateRel(bonePtr[1], bonePtr[2], bonePtr[3]);
phd_RotYXZsuperpack(&rot, 0);
if( track ) {
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(ID_SKIDOO_LARA, (flags & 3) == 1 ? 1 : 7);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons(*track, clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
track = NULL;
} else {
#ifdef FEATURE_VIDEOFX_IMPROVED
SetMeshReflectState(CHK_ANY(flags, 4) ? (int)ID_SKIDOO_ARMED : item->objectID, i);
#endif // FEATURE_VIDEOFX_IMPROVED
phd_PutPolygons(meshPtr[i], clip);
#ifdef FEATURE_VIDEOFX_IMPROVED
ClearMeshReflectState();
#endif // FEATURE_VIDEOFX_IMPROVED
}
bonePtr += 4;
}
}
}
phd_PopMatrix();
}
/*
* Inject function
*/
void Inject_Skidoo() {
// INJECT(0x0043CEE0, InitialiseSkidoo);
// INJECT(0x0043CF20, SkidooCheckGeton);
// INJECT(0x0043D010, SkidooCollision);
// INJECT(0x0043D110, SkidooBaddieCollision);
// INJECT(0x0043D310, TestHeight);
// INJECT(0x0043D3D0, DoShift);
// INJECT(0x0043D650, DoDynamics);
// INJECT(0x0043D6B0, GetCollisionAnim);
INJECT(0x0043D740, DoSnowEffect);
// INJECT(0x0043D880, SkidooDynamics);
// INJECT(0x0043DD20, SkidooUserControl);
// INJECT(0x0043DEE0, SkidooCheckGetOffOK);
// INJECT(0x0043DFF0, SkidooAnimation);
INJECT(0x0043E2D0, SkidooExplode);
// INJECT(0x0043E350, SkidooCheckGetOff);
INJECT(0x0043E590, SkidooGuns);
// INJECT(0x0043E6B0, SkidooControl);
INJECT(0x0043EB10, DrawSkidoo);
// INJECT(0x0043EDF0, InitialiseSkidman);
// INJECT(0x0043EE80, SkidManControl);
// INJECT(0x0043F280, SkidmanPush);
// INJECT(0x0043F3A0, SkidmanCollision);
}
================================================
FILE: game/skidoo.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef SKIDOO_H_INCLUDED
#define SKIDOO_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define InitialiseSkidoo ((void(__cdecl*)(__int16)) 0x0043CEE0)
// 0x0043CF20: SkidooCheckGeton
// 0x0043D010: SkidooCollision
// 0x0043D110: SkidooBaddieCollision
// 0x0043D310: TestHeight
// 0x0043D3D0: DoShift
// 0x0043D650: DoDynamics
// 0x0043D6B0: GetCollisionAnim
void __cdecl DoSnowEffect(ITEM_INFO *item); // 0x0043D740
// 0x0043D880: SkidooDynamics
// 0x0043DD20: SkidooUserControl
// 0x0043DEE0: SkidooCheckGetOffOK
// 0x0043DFF0: SkidooAnimation
void __cdecl SkidooExplode(ITEM_INFO *item); // 0x0043E2D0
// 0x0043E350: SkidooCheckGetOff
void __cdecl SkidooGuns(); // 0x0043E590
// 0x0043E6B0: SkidooControl
void __cdecl DrawSkidoo(ITEM_INFO *item);
#define InitialiseSkidman ((void(__cdecl*)(__int16)) 0x0043EDF0)
#define SkidManControl ((void(__cdecl*)(__int16)) 0x0043EE80)
// 0x0043F280: SkidmanPush
#define SkidmanCollision ((void(__cdecl*)(__int16, ITEM_INFO *, COLL_INFO *)) 0x0043F3A0)
#endif // SKIDOO_H_INCLUDED
================================================
FILE: game/sound.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/sound.h"
#include "specific/sndpc.h"
#include "global/vars.h"
int __cdecl GetRealTrack(int trackID) {
static char vtracks[] = {2, 19, 20, 26, -1};
int idx = 0;
int track = 2;
for( int i = 2; i < trackID; ++i ) {
if( (vtracks[idx] >= 0) && (i == vtracks[idx]) )
++idx;
else
++track;
}
return track;
}
void __cdecl SOUND_Init() {
S_SoundSetMasterVolume(32); // 50% sfx volume
for( int i=0; i<32; ++i )
SfxInfos[i].sampleIdx = -1;
SoundIsActive = TRUE;
}
/*
* Inject function
*/
void Inject_Sound() {
INJECT(0x0043F430, GetRealTrack);
// INJECT(0x0043F470, PlaySoundEffect);
// INJECT(0x0043F910, StopSoundEffect);
// INJECT(0x0043F970, SOUND_EndScene);
// INJECT(0x0043FA00, SOUND_Stop);
INJECT(0x0043FA30, SOUND_Init);
}
================================================
FILE: game/sound.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef SOUND_H_INCLUDED
#define SOUND_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
int __cdecl GetRealTrack(int trackID); // 0x0043F430
#define PlaySoundEffect ((void(__cdecl*)(DWORD, PHD_3DPOS *, DWORD)) 0x0043F470)
#define StopSoundEffect ((void(__cdecl*)(int)) 0x0043F910)
#define SOUND_EndScene ((void(__cdecl*)(void)) 0x0043F970)
#define SOUND_Stop ((void(__cdecl*)(void)) 0x0043FA00)
void __cdecl SOUND_Init(); // 0x0043FA30
#endif // SOUND_H_INCLUDED
================================================
FILE: game/sphere.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/sphere.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Sphere() {
// INJECT(0x0043FA60, TestCollision);
// INJECT(0x0043FB90, GetSpheres);
// INJECT(0x0043FE70, GetJointAbsPosition);
// INJECT(0x00440010, BaddieBiteEffect);
}
================================================
FILE: game/sphere.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef SPHERE_H_INCLUDED
#define SPHERE_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x0043FA60: TestCollision
// 0x0043FB90: GetSpheres
#define GetJointAbsPosition ((void(__cdecl*)(ITEM_INFO*, PHD_VECTOR*, int)) 0x0043FE70)
#define BaddieBiteEffect ((void(__cdecl*)(ITEM_INFO*,BITE_INFO*)) 0x00440010)
#endif // SPHERE_H_INCLUDED
================================================
FILE: game/spider.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/spider.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Spider() {
// INJECT(0x00440070, SpiderLeap);
// INJECT(0x00440120, SpiderControl);
// INJECT(0x00440340, BigSpiderControl);
}
================================================
FILE: game/spider.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef SPIDER_H_INCLUDED
#define SPIDER_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// 0x00440070: SpiderLeap
#define SpiderControl ((void(__cdecl*)(__int16)) 0x00440120)
#define BigSpiderControl ((void(__cdecl*)(__int16)) 0x00440340)
#endif // SPIDER_H_INCLUDED
================================================
FILE: game/text.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/text.h"
#include "3dsystem/scalespr.h"
#include "game/health.h"
#include "specific/frontend.h"
#include "specific/output.h"
#include "global/vars.h"
#ifdef FEATURE_HUD_IMPROVED
#include "modding/texture_utils.h"
extern DWORD InvTextBoxMode;
#endif // FEATURE_HUD_IMPROVED
#define IS_CHAR_SECRET(x) ((x) >= CHAR_SECRET1 && (x) <= CHAR_SECRET3)
#define IS_CHAR_LEGAL(x) ((x) <= 0x12 || ((x) >= 0x20 && (x) <= 0x7F) || IS_CHAR_SECRET(x))
#define IS_CHAR_DIACRITIC(x) ((x) == '(' || (x) == ')' || (x) == '$' || (x) == '~')
static const BYTE T_TextSpacing[0x6E] = {
// A B C D E F G H
14, 11, 11, 11, 11, 11, 11, 13,
// I J K L M N O P
8, 11, 12, 11, 13, 13, 12, 11,
// Q R S T U V W X
12, 12, 11, 12, 13, 13, 13, 12,
// Y Z a b c d e f
12, 11, 9, 9, 9, 9, 9, 9,
// g h i j k l m n
9, 9, 5, 9, 9, 5, 12, 10,
// o p q r s t u v
9, 9, 9, 8, 9, 8, 9, 9,
// w x y z 0 1 2 3
11, 9, 9, 9, 12, 8, 10, 10,
// 4 5 6 7 8 9 . ,
10, 10, 10, 9, 10, 10, 5, 5,
// ! ? [ di" / di^ di' -
5, 11, 9, 10, 8, 6, 6, 7,
// + : ] \ ` # '
7, 3, 11, 8, 13, 16, 9, 4,
// arU arD dg0 dg1 dg2 dg3 dg4 dg5
12, 12, 7, 5, 7, 7, 7, 7,
// dg6 dg7 dg8 dg9 js1 js2 js3 js4
7, 7, 7, 7, 16, 14, 14, 14,
// js5 js6 js7 js8 js9 sc0 sc1 sc2
16, 16, 16, 16, 16, 12, 14, 8,
// sc3 sc4 dwn up lft rht
8, 8, 8, 8, 8, 8,
};
static const BYTE T_RemapASCII[0x5F] = {
// nop ! "to [ # $to di` %to &to # '
0x00, 0x40, 0x42, 0x4E, 0x4D, 0x4A, 0x4E, 0x4F,
// (to di^ )to di' *to js1 + , - . /
0x45, 0x46, 0x5C, 0x48, 0x3F, 0x47, 0x3E, 0x44,
// 0 1 2 3 4 5 6 7
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
// 8 9 : ;to : to ] ?
0x3C, 0x3D, 0x49, 0x49, 0x42, 0x4A, 0x4B, 0x41,
// @to A A B C D E F G
0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
// H I J K L M N O
0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
// P Q R S T U V W
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
// X Y Z [to arU \ ]to arD ^to js6 _to js7
0x17, 0x18, 0x19, 0x50, 0x4C, 0x51, 0x61, 0x62,
// ` a b c d e f g
0x4D, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
// h i j k l m n o
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
// p q r s t u v w
0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
// x y z {to js9 |to sc0 }to sc1 ~to di"
0x31, 0x32, 0x33, 0x64, 0x65, 0x66, 0x43,
};
void __cdecl T_InitPrint() {
DisplayModeInfo(NULL);
for( int i=0; i<64; ++i )
TextInfoTable[i].flags = 0;
TextStringCount = 0;
}
TEXT_STR_INFO *__cdecl T_Print(int x, int y, __int16 z, const char *str) {
if( str == NULL || TextStringCount >= 64 )
return NULL;
for( int i=0; i<64; ++i ) {
if( !CHK_ANY(TextInfoTable[i].flags, TIF_Active) ) {
int stringLen = T_GetStringLen(str);
CLAMPG(stringLen, 64); // NOTE: useless check, but decided to leave it here
TextInfoTable[i].scaleH = PHD_ONE;
TextInfoTable[i].scaleV = PHD_ONE;
#ifdef FEATURE_HUD_IMPROVED
TextInfoTable[i].xPos = x;
TextInfoTable[i].yPos = y;
#else // FEATURE_HUD_IMPROVED
TextInfoTable[i].xPos = x * GetTextScaleH(PHD_ONE) / PHD_ONE;
TextInfoTable[i].yPos = y * GetTextScaleV(PHD_ONE) / PHD_ONE;
#endif // FEATURE_HUD_IMPROVED
TextInfoTable[i].zPos = z;
TextInfoTable[i].letterSpacing = 1;
TextInfoTable[i].wordSpacing = 6;
TextInfoTable[i].textFlags = 0;
TextInfoTable[i].outlFlags = 0;
TextInfoTable[i].bgndFlags = 0;
TextInfoTable[i].bgndSizeX = 0;
TextInfoTable[i].bgndSizeY = 0;
TextInfoTable[i].bgndOffX = 0;
TextInfoTable[i].bgndOffY = 0;
TextInfoTable[i].bgndOffZ = 0;
TextInfoTable[i].flags = TIF_Active;
TextInfoTable[i].pString = TheStrings[i].str;
memcpy(TheStrings[i].str, str, stringLen);
++TextStringCount;
return &TextInfoTable[i];
}
}
return NULL;
}
void __cdecl T_ChangeText(TEXT_STR_INFO *textInfo, const char *newString) {
if( newString == NULL || textInfo == NULL || !CHK_ANY(textInfo->flags, TIF_Active) )
return;
// NOTE: the original code was unsafe crap. Reimplemented it a little safer
strncpy(textInfo->pString, newString, 64);
if( T_GetStringLen(newString) >= 64 )
textInfo->pString[63] = 0;
}
void __cdecl T_SetScale(TEXT_STR_INFO *textInfo, int scaleH, int scaleV) {
if( textInfo != NULL ) {
textInfo->scaleH = scaleH;
textInfo->scaleV = scaleV;
}
}
void __cdecl T_FlashText(TEXT_STR_INFO *textInfo, __int16 state, __int16 rate) {
if( textInfo == NULL)
return;
if( state == 0 ) {
textInfo->flags &= ~TIF_Flash;
} else {
textInfo->flags |= TIF_Flash;
textInfo->flashRate = rate;
textInfo->flashCount = rate;
}
}
void __cdecl T_AddBackground(TEXT_STR_INFO *textInfo, __int16 xSize, __int16 ySize, __int16 xOff, __int16 yOff, __int16 zOff, INV_COLOURS invColour, GOURAUD_FILL *gour, UINT16 flags) {
if( textInfo == NULL )
return;
textInfo->flags |= TIF_Bgnd;
#ifdef FEATURE_HUD_IMPROVED
textInfo->bgndSizeX = xSize;
textInfo->bgndSizeY = ySize;
textInfo->bgndOffX = xOff;
textInfo->bgndOffY = yOff;
#else // FEATURE_HUD_IMPROVED
DWORD scaleH = GetTextScaleH(textInfo->scaleH);
DWORD scaleV = GetTextScaleV(textInfo->scaleV);
textInfo->bgndSizeX = scaleH * xSize / PHD_ONE;
textInfo->bgndSizeY = scaleV * ySize / PHD_ONE;
textInfo->bgndOffX = scaleH * xOff / PHD_ONE;
textInfo->bgndOffY = scaleV * yOff / PHD_ONE;
#endif // FEATURE_HUD_IMPROVED
textInfo->bgndOffZ = zOff;
textInfo->bgndColor = invColour;
textInfo->bgndGour = gour;
textInfo->bgndFlags = flags;
}
void __cdecl T_RemoveBackground(TEXT_STR_INFO *textInfo) {
if( textInfo != NULL )
textInfo->flags &= ~TIF_Bgnd;
}
void __cdecl T_AddOutline(TEXT_STR_INFO *textInfo, BOOL state, INV_COLOURS invColour, GOURAUD_OUTLINE *gour, UINT16 flags) {
if( textInfo != NULL ) {
textInfo->flags |= TIF_Outline;
textInfo->outlColour = invColour;
textInfo->outlGour = gour;
textInfo->outlFlags = flags;
}
}
void __cdecl T_RemoveOutline(TEXT_STR_INFO *textInfo) {
if( textInfo != NULL )
textInfo->flags &= ~TIF_Outline;
}
void __cdecl T_CentreH(TEXT_STR_INFO *textInfo, UINT16 state) {
if( textInfo != NULL ) {
if( state )
textInfo->flags |= TIF_CentreH;
else
textInfo->flags &= ~TIF_CentreH;
}
}
void __cdecl T_CentreV(TEXT_STR_INFO *textInfo, UINT16 state) {
if( textInfo != NULL ) {
if( state )
textInfo->flags |= TIF_CentreV;
else
textInfo->flags &= ~TIF_CentreV;
}
}
void __cdecl T_RightAlign(TEXT_STR_INFO *textInfo, bool state) {
if( textInfo == NULL)
return;
if( state ) {
textInfo->flags |= TIF_Right;
} else {
textInfo->flags &= ~TIF_Right;
}
}
void __cdecl T_BottomAlign(TEXT_STR_INFO *textInfo, bool state) {
if( textInfo == NULL)
return;
if( state ) {
textInfo->flags |= TIF_Bottom;
} else {
textInfo->flags &= ~TIF_Bottom;
}
}
void __cdecl T_DrawTextBox(int sx, int sy, int z, int width, int height) {
int x0, y0, x1, y1, offset;
int scaleH, scaleV;
int meshIdx = Objects[ID_TEXT_BOX].meshIndex;
#ifdef FEATURE_HUD_IMPROVED
offset = GetRenderScale(4);
scaleH = GetRenderScale(PHD_ONE);
scaleV = GetRenderScale(PHD_ONE);
#else // !FEATURE_HUD_IMPROVED
offset = 4;
scaleH = PHD_ONE;
scaleV = PHD_ONE;
#endif // FEATURE_HUD_IMPROVED
x0 = sx + offset;
y0 = sy + offset;
x1 = sx - offset + width;
y1 = sy - offset + height;
width = PHD_ONE * (width - offset * 2) / 8;
height = PHD_ONE * (height - offset * 2) / 8;
S_DrawScreenSprite2d(x0, y0, z, scaleH, scaleV, (meshIdx + 0), 0x1000, 0);
S_DrawScreenSprite2d(x1, y0, z, scaleH, scaleV, (meshIdx + 1), 0x1000, 0);
S_DrawScreenSprite2d(x1, y1, z, scaleH, scaleV, (meshIdx + 2), 0x1000, 0);
S_DrawScreenSprite2d(x0, y1, z, scaleH, scaleV, (meshIdx + 3), 0x1000, 0);
S_DrawScreenSprite2d(x0, y0, z, width, scaleV, (meshIdx + 4), 0x1000, 0);
S_DrawScreenSprite2d(x1, y0, z, scaleH, height, (meshIdx + 5), 0x1000, 0);
S_DrawScreenSprite2d(x0, y1, z, width, scaleV, (meshIdx + 6), 0x1000, 0);
S_DrawScreenSprite2d(x0, y0, z, scaleH, height, (meshIdx + 7), 0x1000, 0);
}
DWORD __cdecl T_GetTextWidth(TEXT_STR_INFO *textInfo) {
int spacing;
DWORD width, scaleH, sprite;
width = 0;
#ifdef FEATURE_HUD_IMPROVED
scaleH = textInfo->scaleH;
#else // FEATURE_HUD_IMPROVED
scaleH = GetTextScaleH(textInfo->scaleH);
#endif // FEATURE_HUD_IMPROVED
for( BYTE *str = (BYTE *)textInfo->pString; *str != 0; str++ ) {
if( !IS_CHAR_LEGAL(*str) || IS_CHAR_DIACRITIC(*str) ) {
continue; // if char code is illegal or not required for width measuring, go to next char
}
if( *str == 0x20 ) { // Check if char is "Space"
// "Space" uses wordSpacing value instead of sprite width
spacing = textInfo->wordSpacing;
}
else if( IS_CHAR_SECRET(*str) ) { // Check if "Secret" sprite
// "Secret" sprites have spacing=16
spacing = 16;
#ifdef FEATURE_HUD_IMPROVED
} else if( *str == 0x7F ) { // Check if it's the opening code of the named sprite sequence
BYTE *ptr = (BYTE *)strchr((const char *)str+1, 0x1F);
if( ptr == NULL ) break; // Closing code is not found, break now!
if( !GetTextSpriteByName((const char *)str+1, ptr-str-1, &sprite, &spacing) ) {
spacing = 0;
}
str = ptr; // move pointer to the sequence end
#endif // FEATURE_HUD_IMPROVED
} else {
if( *str < 0x0B ) { // Check if "Digit" sprite
sprite = *str + 0x51; // We have (*str >= 0x01) here. "Digit" sprite codes start from (0x52 = 0x01 + 0x51)
}
else if( *str <= 0x12 ) { // Check if "Special" sprite. NOTE: original code was (*str < 0x10) but this was wrong
// Check if normal *str or "Special" sprite
sprite = *str + 0x5B; // We have (*str >= 0x0B) here. "Special" sprite codes start from (0x66 = 0x0B + 0x5B)
} else { // here (*str > 0x20)
sprite = T_RemapASCII[*str - 0x20]; // For normal letters we have sprite code table
}
// Check if normal letter sprite has digit representation
if( *str >= '0' && *str <= '9' ) { // NOTE: original code was (sprite >= '0' && sprite <= '9') but this was wrong
// Normal letter sprites with digits have spacing=12
spacing = 12;
} else {
// For "Digit", "Special" and normal letter sprites we use spacing table + letterSpacing
#if defined(FEATURE_HUD_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
spacing = GetTexPagesGlyphSpacing(sprite);
if( !spacing ) spacing = T_TextSpacing[sprite];
#else // defined(FEATURE_HUD_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
spacing = T_TextSpacing[sprite];
#endif // defined(FEATURE_HUD_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
// NOTE: this condition was added instead of returned value recalculation (see below).
// In the original code spacing addition was unconditional
if( str[1] != 0 ) { // If this letter is not last, add letterSpacing
spacing += textInfo->letterSpacing;
}
}
}
width += spacing * scaleH / PHD_ONE;
}
// NOTE: original code was ((width - textInfo->letterSpacing) & ~1) but this was wrong, because letterSpacing is not scaled
// And also we calculate width of any string, there may not be letterSpacing at all (i.e. digit letter sprites )
return width;
}
BOOL __cdecl T_RemovePrint(TEXT_STR_INFO *textInfo) {
if( textInfo == NULL || !CHK_ANY(textInfo->flags, TIF_Active) )
return false;
textInfo->flags &= ~TIF_Active;
--TextStringCount;
return true;
}
__int16 __cdecl T_GetStringLen(const char *str) {
// Calculates string length up to 64 chars including null terminator
for( int i=0; i<64; ++i ) {
if( str[i] == 0 )
return i+1;
}
return 64;
}
void __cdecl T_DrawText() {
for( int i=0; i<64; ++i ) {
if( CHK_ANY(TextInfoTable[i].flags, TIF_Active) )
T_DrawThisText(&TextInfoTable[i]);
}
}
void __cdecl T_DrawThisText(TEXT_STR_INFO *textInfo) {
int x, y, z, xOff, spacing;
int boxX, boxY, boxZ, boxW, boxH;
DWORD textWidth, scaleH, scaleV, sprite;
#ifdef FEATURE_HUD_IMPROVED
int sx, sy, sh, sv;
if( CHK_ANY(textInfo->flags, TIF_Hide) ) {
return;
}
scaleH = textInfo->scaleH;
scaleV = textInfo->scaleV;
#else // FEATURE_HUD_IMPROVED
scaleH = GetTextScaleH(textInfo->scaleH);
scaleV = GetTextScaleV(textInfo->scaleV);
#endif // FEATURE_HUD_IMPROVED
// Do text flashing if required
if( CHK_ANY(textInfo->flags, TIF_Flash) ) {
textInfo->flashCount -= (__int16)Camera.numberFrames;
if( textInfo->flashCount <= -textInfo->flashRate ) {
textInfo->flashCount = textInfo->flashRate;
}
else if ( textInfo->flashCount < 0 ) {
return;
}
}
x = textInfo->xPos;
y = textInfo->yPos;
z = textInfo->zPos;
textWidth = T_GetTextWidth(textInfo);
#ifdef FEATURE_HUD_IMPROVED
// Horizontal alignment
if( CHK_ANY(textInfo->flags, TIF_CentreH) ) {
x += (GetRenderWidthDownscaled() - textWidth) / 2;
}
else if( CHK_ANY(textInfo->flags, TIF_Right) ) {
x += GetRenderWidthDownscaled() - textWidth;
}
// Vertical alignment
if( CHK_ANY(textInfo->flags, TIF_CentreV) ) {
y += GetRenderHeightDownscaled() / 2;
}
else if ( CHK_ANY(textInfo->flags, TIF_Bottom) ) {
y += GetRenderHeightDownscaled();
}
#else // FEATURE_HUD_IMPROVED
// Horizontal alignment
if( CHK_ANY(textInfo->flags, TIF_CentreH) ) {
x += (GetRenderWidth() - textWidth) / 2;
}
else if( CHK_ANY(textInfo->flags, TIF_Right) ) {
x += GetRenderWidth() - textWidth;
}
// Vertical alignment
if( CHK_ANY(textInfo->flags, TIF_CentreV) ) {
y += GetRenderHeight() / 2;
}
else if ( CHK_ANY(textInfo->flags, TIF_Bottom) ) {
y += GetRenderHeight();
}
#endif // FEATURE_HUD_IMPROVED
boxX = x + textInfo->bgndOffX - (2 * scaleH / PHD_ONE);
boxY = y + textInfo->bgndOffY - (4 * scaleV / PHD_ONE) - (11 * scaleV / PHD_ONE);
boxZ = z + textInfo->bgndOffZ + 2;
for( BYTE *str = (BYTE *)textInfo->pString; *str != 0; str++ ) {
// Check if char code is in illegal range
if( !IS_CHAR_LEGAL(*str) )
continue;
if( *str == 0x20 ) { // Check if char is "Space"
// "Space" uses wordSpacing value instead of sprite width
x += textInfo->wordSpacing * scaleH / PHD_ONE;
}
else if( IS_CHAR_SECRET(*str) ) { // Check if "Secret" sprite
// Draw "Secret" sprite
#ifdef FEATURE_HUD_IMPROVED
sx = GetTextScaleH(x + 10);
sy = GetTextScaleV(y);
S_DrawPickup(sx, sy, 0x1BE8, Objects[ID_SECRET1 + (*str - CHAR_SECRET1)].meshIndex, 0x1000);
#else // FEATURE_HUD_IMPROVED
S_DrawPickup(x + 10, y, 0x1BE8, Objects[ID_SECRET1 + (*str - CHAR_SECRET1)].meshIndex, 0x1000);
#endif // FEATURE_HUD_IMPROVED
// "Secret" sprites have spacing=16
x += 16 * scaleH / PHD_ONE;
#ifdef FEATURE_HUD_IMPROVED
} else if( *str == 0x7F ) { // Check if it's the opening code of the named sprite sequence
BYTE *ptr = (BYTE *)strchr((const char *)str+1, 0x1F);
if( ptr == NULL ) break; // Closing code is not found, break now!
if( GetTextSpriteByName((const char *)str+1, ptr-str-1, &sprite, &xOff) ) {
if( x > 0 && x < GetRenderWidthDownscaled() && y > 0 && y < GetRenderHeightDownscaled() ) {
sx = GetTextScaleH(x);
sy = GetTextScaleV(y);
sh = GetTextScaleH(scaleH);
sv = GetTextScaleV(scaleV);
S_DrawScreenSprite2d(sx, sy, z, sh, sv, sprite, 0x1000, textInfo->textFlags);
}
x += xOff * scaleH / PHD_ONE;
}
str = ptr; // move pointer to the sequence end
#endif // FEATURE_HUD_IMPROVED
} else {
if( *str < 0x0B ) { // Check if "Digit" sprite
sprite = *str + 0x51; // We have (*str >= 0x01) here. "Digit" sprite codes start from (0x52 = 0x01 + 0x51)
}
else if( *str <= 0x12 ) { // Check if "Special" sprite. NOTE: original code was (*str < 0x10) but this was wrong
// Check if normal *str or "Special" sprite
sprite = *str + 0x5B; // We have (*str >= 0x0B) here. "Special" sprite codes start from (0x66 = 0x0B + 0x5B)
} else { // here (*str > 0x20)
sprite = T_RemapASCII[*str - 0x20]; // For normal letters we have sprite code table
}
// Check if normal letter sprite is digit representation
// Normal letter sprites with digits have spacing=12
// But sprite itself is center aligned in this space
if( *str >= '0' && *str <= '9' ) {
// !!! Here we do LEFT spacing part for digit letters !!!
#if defined(FEATURE_HUD_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
spacing = GetTexPagesGlyphSpacing(sprite);
if( !spacing ) spacing = T_TextSpacing[sprite];
#else // defined(FEATURE_HUD_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
spacing = T_TextSpacing[sprite];
#endif // defined(FEATURE_HUD_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
xOff = (12 - spacing) / 2;
x += xOff * scaleH / PHD_ONE;
}
// Draw letter sprite
#ifdef FEATURE_HUD_IMPROVED
if( x > 0 && x < GetRenderWidthDownscaled() && y > 0 && y < GetRenderHeightDownscaled() ) {
#if (DIRECT3D_VERSION >= 0x900)
sx = GetTextScaleH(x + GetTexPagesGlyphXOffset(sprite));
sy = GetTextScaleV(y + GetTexPagesGlyphYOffset(sprite));
sh = GetTextScaleH(scaleH * GetTexPagesGlyphXStretch(sprite));
sv = GetTextScaleV(scaleV * GetTexPagesGlyphYStretch(sprite));
#else // (DIRECT3D_VERSION >= 0x900)
sx = GetTextScaleH(x);
sy = GetTextScaleV(y);
sh = GetTextScaleH(scaleH);
sv = GetTextScaleV(scaleV);
#endif // (DIRECT3D_VERSION >= 0x900)
S_DrawScreenSprite2d(sx, sy, z, sh, sv, (Objects[ID_ALPHABET].meshIndex + sprite), 0x1000, textInfo->textFlags);
}
#else // FEATURE_HUD_IMPROVED
if( x > 0 && x < GetRenderWidth() && y > 0 && y < GetRenderHeight() ) {
S_DrawScreenSprite2d(x, y, z, scaleH, scaleV, (Objects[ID_ALPHABET].meshIndex + sprite), 0x1000, textInfo->textFlags);
}
#endif // FEATURE_HUD_IMPROVED
// Check if letter is diacritic
// Diacritics are drawn right on the next letter sprite, so there is no spacing for them
if( IS_CHAR_DIACRITIC(*str) )
continue;
// Check if normal letter sprite is digit representation
#if defined(FEATURE_HUD_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
spacing = GetTexPagesGlyphSpacing(sprite);
if( !spacing ) spacing = T_TextSpacing[sprite];
#else // defined(FEATURE_HUD_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
spacing = T_TextSpacing[sprite];
#endif // defined(FEATURE_HUD_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
if( *str >= '0' && *str <= '9' ) {
// !!! Here we do RIGHT spacing part for digit letters !!!
xOff = (12 - spacing) / 2;
x += (12 - xOff) * scaleH / PHD_ONE;
} else {
// For "Digit", "Special" and normal letter sprites we use spacing table + letterSpacing
xOff = spacing;
xOff += textInfo->letterSpacing;
x += xOff * scaleH / PHD_ONE;
}
}
}
// Draw background/outline if required
if( CHK_ANY(textInfo->flags, TIF_Bgnd|TIF_Outline) ) {
if( textInfo->bgndSizeX != 0 ) {
boxX += ((int)textWidth - textInfo->bgndSizeX) / 2;
boxW = textInfo->bgndSizeX + 4;
} else {
boxW = textWidth + 4;
}
if( textInfo->bgndSizeY != 0 ) {
boxH = textInfo->bgndSizeY;
} else {
#ifdef FEATURE_HUD_IMPROVED
boxH = (SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode ? 14 : 16) * scaleV / PHD_ONE;
#else // !FEATURE_HUD_IMPROVED
boxH = 16 * scaleV / PHD_ONE;
#endif // FEATURE_HUD_IMPROVED
}
#ifdef FEATURE_HUD_IMPROVED
sx = GetTextScaleH(boxX);
sy = GetTextScaleV(boxY);
sh = GetTextScaleH(boxW);
sv = GetTextScaleV(boxH);
// Draw background
if( CHK_ANY(textInfo->flags, TIF_Bgnd) ) {
S_DrawScreenFBox(sx, sy, boxZ, sh, sv, textInfo->bgndColor, textInfo->bgndGour, textInfo->bgndFlags);
}
// Draw outline
if( CHK_ANY(textInfo->flags, TIF_Outline) ) {
if( SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode ) {
S_DrawScreenBox(sx, sy, boxZ, sh, sv, textInfo->outlColour, textInfo->outlGour, textInfo->outlFlags);
} else {
T_DrawTextBox(sx, sy, z, sh, sv);
}
}
#else // FEATURE_HUD_IMPROVED
// Draw background
if( CHK_ANY(textInfo->flags, TIF_Bgnd) ) {
S_DrawScreenFBox(boxX, boxY, boxZ, boxW, boxH, textInfo->bgndColor, textInfo->bgndGour, textInfo->bgndFlags);
}
// Draw outline
if( CHK_ANY(textInfo->flags, TIF_Outline) ) {
T_DrawTextBox(boxX, boxY, z, boxW, boxH);
}
#endif // FEATURE_HUD_IMPROVED
}
}
DWORD __cdecl GetTextScaleH(DWORD baseScale) {
#ifdef FEATURE_HUD_IMPROVED
return GetRenderScale(baseScale);
#else // !FEATURE_HUD_IMPROVED
DWORD renderWidth, renderScale;
renderWidth = GetRenderWidth();
CLAMPL(renderWidth, 640)
renderScale = renderWidth * PHD_ONE / 640;
return (baseScale / PHD_HALF) * (renderScale / PHD_HALF);
#endif // FEATURE_HUD_IMPROVED
}
DWORD __cdecl GetTextScaleV(DWORD baseScale) {
#ifdef FEATURE_HUD_IMPROVED
return GetRenderScale(baseScale);
#else // !FEATURE_HUD_IMPROVED
DWORD renderHeight, renderScale;
renderHeight = GetRenderHeight();
CLAMPL(renderHeight, 480)
renderScale = renderHeight * PHD_ONE / 480;
return (baseScale / PHD_HALF) * (renderScale / PHD_HALF);
#endif // FEATURE_HUD_IMPROVED
}
#ifdef FEATURE_HUD_IMPROVED
void T_HideText(TEXT_STR_INFO *textInfo, __int16 state) {
if( textInfo == NULL)
return;
if( state == 0 ) {
textInfo->flags &= ~TIF_Hide;
} else {
textInfo->flags |= TIF_Hide;
}
}
#endif // FEATURE_HUD_IMPROVED
/*
* Inject function
*/
void Inject_Text() {
INJECT(0x00440500, T_InitPrint);
INJECT(0x00440530, T_Print);
INJECT(0x00440640, T_ChangeText);
INJECT(0x00440680, T_SetScale);
INJECT(0x004406A0, T_FlashText);
INJECT(0x004406D0, T_AddBackground);
INJECT(0x00440760, T_RemoveBackground);
INJECT(0x00440770, T_AddOutline);
INJECT(0x004407A0, T_RemoveOutline);
INJECT(0x004407B0, T_CentreH);
INJECT(0x004407D0, T_CentreV);
INJECT(0x004407F0, T_RightAlign);
INJECT(0x00440810, T_BottomAlign);
INJECT(0x00440830, T_GetTextWidth);
INJECT(0x00440940, T_RemovePrint);
INJECT(0x00440970, T_GetStringLen);
INJECT(0x004409A0, T_DrawText);
INJECT(0x004409D0, T_DrawTextBox);
INJECT(0x00440B60, T_DrawThisText);
INJECT(0x00440F40, GetTextScaleH);
INJECT(0x00440F80, GetTextScaleV);
}
================================================
FILE: game/text.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef TEXT_H_INCLUDED
#define TEXT_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl T_InitPrint(); // 0x00440500
TEXT_STR_INFO *__cdecl T_Print(int x, int y, __int16 z, const char *str); // 0x00440530
void __cdecl T_ChangeText(TEXT_STR_INFO *textInfo, const char *newString); // 0x00440640
void __cdecl T_SetScale(TEXT_STR_INFO *textInfo, int scaleH, int scaleV); // 0x00440680
void __cdecl T_FlashText(TEXT_STR_INFO *textInfo, __int16 state, __int16 rate); // 0x004406A0
void __cdecl T_AddBackground(TEXT_STR_INFO *textInfo, __int16 xSize, __int16 ySize, __int16 xOff, __int16 yOff, __int16 zOff, INV_COLOURS invColour, GOURAUD_FILL *gour, UINT16 flags); // 0x004406D0
void __cdecl T_RemoveBackground(TEXT_STR_INFO *textInfo); // 0x00440760
void __cdecl T_AddOutline(TEXT_STR_INFO *textInfo, BOOL state, INV_COLOURS invColour, GOURAUD_OUTLINE *gour, UINT16 flags); // 0x00440770
void __cdecl T_RemoveOutline(TEXT_STR_INFO *textInfo); // 0x004407A0
void __cdecl T_CentreH(TEXT_STR_INFO *textInfo, UINT16 state); // 0x004407B0
void __cdecl T_CentreV(TEXT_STR_INFO *textInfo, UINT16 state); // 0x004407D0
void __cdecl T_RightAlign(TEXT_STR_INFO *textInfo, bool state); // 0x004407F0
void __cdecl T_BottomAlign(TEXT_STR_INFO *textInfo, bool state); // 0x00440810
DWORD __cdecl T_GetTextWidth(TEXT_STR_INFO *textInfo); // 0x00440830
BOOL __cdecl T_RemovePrint(TEXT_STR_INFO *textInfo); // 0x00440940
__int16 __cdecl T_GetStringLen(const char *str); // 0x00440970
void __cdecl T_DrawText(); // 0x004409A0
void __cdecl T_DrawTextBox(int sx, int sy, int z, int width, int height); // 0x004409D0
void __cdecl T_DrawThisText(TEXT_STR_INFO *textInfo); // 0x00440B60
DWORD __cdecl GetTextScaleH(DWORD baseScale); // 0x00440F40
DWORD __cdecl GetTextScaleV(DWORD baseScale); // 0x00440F80
#ifdef FEATURE_HUD_IMPROVED
void T_HideText(TEXT_STR_INFO *textInfo, __int16 state);
#endif // FEATURE_HUD_IMPROVED
#endif // TEXT_H_INCLUDED
================================================
FILE: game/traps.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Copyright (c) 2020 ChocolateFan
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/traps.h"
#include "3dsystem/phd_math.h"
#include "game/control.h"
#include "game/effects.h"
#include "game/items.h"
#include "game/missile.h"
#include "game/sound.h"
#include "game/sphere.h"
#include "specific/game.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/joy_output.h"
#endif // FEATURE_INPUT_IMPROVED
void __cdecl MineControl(__int16 mineID) {
ITEM_INFO *mine = &Items[mineID];
if( CHK_ANY(mine->flags, IFL_INVISIBLE) ) {
return;
}
if( !MinesDetonated ) {
__int16 roomNumber = mine->roomNumber;
GetFloor(mine->pos.x, mine->pos.y - 0x800, mine->pos.z, &roomNumber);
ITEM_INFO *item = NULL;
__int16 itemID = RoomInfo[roomNumber].itemNumber;
for( ; itemID >= 0; itemID = item->nextItem ) {
item = &Items[itemID];
if( item->objectID == ID_BOAT ) {
int x = item->pos.x - mine->pos.x;
int y = item->pos.z - mine->pos.z;
if( SQR(x) + SQR(y) < SQR(0x200) ) {
break;
}
}
}
if( itemID < 0 ) {
return;
}
if( Lara.skidoo == itemID ) {
ExplodingDeath(Lara.item_number, ~0, 0);
LaraItem->hitPoints = 0;
LaraItem->flags |= IFL_INVISIBLE;
}
item->objectID = ID_BOAT_BITS;
ExplodingDeath(itemID, ~0, 0);
KillItem(itemID);
item->objectID = ID_BOAT;
FLOOR_INFO *floor = GetFloor(mine->pos.x, mine->pos.y, mine->pos.z, &roomNumber);
GetHeight(floor, mine->pos.x, mine->pos.y, mine->pos.z);
TestTriggers(TriggerPtr, 1);
MinesDetonated = 1;
} else if ( GetRandomControl() < 0x7800 ) {
return;
}
__int16 fxID = CreateEffect(mine->roomNumber);
if ( fxID != -1 )
{
FX_INFO *fx = &Effects[fxID];
fx->pos.x = mine->pos.x;
fx->pos.y = mine->pos.y - 0x400;
fx->pos.z = mine->pos.z;
fx->speed = 0;
fx->frame_number = 0;
fx->counter = 0;
fx->object_number = ID_EXPLOSION;
}
Splash(mine);
PlaySoundEffect(105, &mine->pos, 0);
mine->flags |= IFL_INVISIBLE;
mine->collidable = 0;
mine->meshBits = 1;
#ifdef FEATURE_INPUT_IMPROVED
JoyRumbleExplode(mine->pos.x, mine->pos.y, mine->pos.z, 0x2800, false);
#endif // FEATURE_INPUT_IMPROVED
}
void __cdecl ControlSpikeWall(__int16 itemID) {
ITEM_INFO *item;
int x, z;
__int16 roomID;
item = &Items[itemID];
if (TriggerActive(item) && item->status != ITEM_DISABLED) {
z = item->pos.z + (16 * phd_cos(item->pos.rotY) >> W2V_SHIFT);
x = item->pos.x + (16 * phd_sin(item->pos.rotY) >> W2V_SHIFT);
roomID = item->roomNumber;
if (GetHeight(GetFloor(x, item->pos.y, z, &roomID), x, item->pos.y, z) != item->pos.y) {
item->status = ITEM_DISABLED;
} else {
item->pos.z = z;
item->pos.x = x;
if (roomID != item->roomNumber)
ItemNewRoom(itemID, roomID);
}
PlaySoundEffect(204, &item->pos, 0);
}
if (item->touchBits) {
LaraItem->hitPoints -= 20;
LaraItem->hit_status = 1;
DoLotsOfBlood(LaraItem->pos.x, LaraItem->pos.y - 512, LaraItem->pos.z, 1, item->pos.rotY, LaraItem->roomNumber, 3);
item->touchBits = 0;
PlaySoundEffect(205, &item->pos, 0);
}
}
void __cdecl ControlCeilingSpikes(__int16 itemID) {
ITEM_INFO *item;
int y;
__int16 roomID;
item = &Items[itemID];
if (TriggerActive(item) && item->status != ITEM_DISABLED) {
y = item->pos.y + 5;
roomID = item->roomNumber;
if (GetHeight(GetFloor(item->pos.x, y, item->pos.z, &roomID), item->pos.x, y, item->pos.z) < y + 1024) {
item->status = ITEM_DISABLED;
} else {
item->pos.y = y;
if (roomID != item->roomNumber)
ItemNewRoom(itemID, roomID);
}
PlaySoundEffect(204, &item->pos, 0);
}
if (item->touchBits) {
LaraItem->hitPoints -= 20;
LaraItem->hit_status = 1;
DoLotsOfBlood(LaraItem->pos.x, LaraItem->pos.y + 768, LaraItem->pos.z, 1, item->pos.rotY, LaraItem->roomNumber, 3);
item->touchBits = 0;
PlaySoundEffect(205, &item->pos, 0);
}
}
void __cdecl HookControl(__int16 itemID) {
ITEM_INFO *item;
static BOOL IsHookHit = FALSE;
item = &Items[itemID];
if (item->touchBits && !IsHookHit) {
LaraItem->hitPoints -= 50;
LaraItem->hit_status = 1;
DoLotsOfBlood(LaraItem->pos.x, LaraItem->pos.y - 512, LaraItem->pos.z, LaraItem->speed, LaraItem->pos.rotY, LaraItem->roomNumber, 3);
} else {
IsHookHit = FALSE;
}
AnimateItem(item);
}
void __cdecl SpinningBlade(__int16 itemID) {
ITEM_INFO *item;
int x, z;
__int16 roomID;
BOOL reverse;
item = &Items[itemID];
if (item->currentAnimState == 2) {
if (item->goalAnimState != 1) {
z = item->pos.z + (1536 * phd_cos(item->pos.rotY) >> W2V_SHIFT);
x = item->pos.x + (1536 * phd_sin(item->pos.rotY) >> W2V_SHIFT);
roomID = item->roomNumber;
if (GetHeight(GetFloor(x, item->pos.y, z, &roomID), x, item->pos.y, z) == NO_HEIGHT)
item->goalAnimState = 1;
}
reverse = TRUE;
if (item->touchBits) {
LaraItem->hit_status = 1;
LaraItem->hitPoints -= 100;
DoLotsOfBlood(LaraItem->pos.x, LaraItem->pos.y - 512, LaraItem->pos.z, 2 * item->speed, LaraItem->pos.rotY, LaraItem->roomNumber, 2);
}
PlaySoundEffect(231, &item->pos, 0);
} else {
if (TriggerActive(item))
item->goalAnimState = 2;
reverse = FALSE;
}
AnimateItem(item);
roomID = item->roomNumber;
item->pos.y = GetHeight(GetFloor(item->pos.x, item->pos.y, item->pos.z, &roomID), item->pos.x, item->pos.y, item->pos.z);
item->floor = item->pos.y;
if (roomID != item->roomNumber)
ItemNewRoom(itemID, roomID);
if (reverse && item->currentAnimState == 1)
item->pos.rotY += PHD_180;
}
void __cdecl IcicleControl(__int16 itemID) {
ITEM_INFO *item;
__int16 roomID;
FLOOR_INFO *floor;
item = &Items[itemID];
switch (item->currentAnimState) {
case 1:
item->goalAnimState = 2;
break;
case 2:
if (!item->gravity) {
item->fallSpeed = 50;
item->gravity = 1;
}
if (item->touchBits) {
LaraItem->hitPoints -= 200;
LaraItem->hit_status = 1;
}
break;
case 3:
item->gravity = 0;
break;
}
AnimateItem(item);
if (item->status == ITEM_DISABLED) {
RemoveActiveItem(itemID);
} else {
roomID = item->roomNumber;
floor = GetFloor(item->pos.x, item->pos.y, item->pos.z, &roomID);
if (item->roomNumber != roomID)
ItemNewRoom(itemID, roomID);
item->floor = GetHeight(floor, item->pos.x, item->pos.y, item->pos.z);
if (item->currentAnimState == 2 && item->pos.y >= item->floor) {
item->gravity = 0;
item->goalAnimState = 3;
item->pos.y = item->floor;
item->fallSpeed = 0;
item->meshBits = 0x2B;
}
}
}
void __cdecl InitialiseBlade(__int16 itemID) {
ITEM_INFO *item;
item = &Items[itemID];
item->animNumber = Objects[ID_BLADE].animIndex + 2;
item->currentAnimState = 1;
item->frameNumber = Anims[item->animNumber].frameBase;
}
void __cdecl BladeControl(__int16 itemID) {
ITEM_INFO *item;
item = &Items[itemID];
if (TriggerActive(item) && item->currentAnimState == 1) {
item->goalAnimState = 2;
} else {
item->goalAnimState = 1;
}
if (CHK_ANY(item->touchBits, 2) && item->currentAnimState == 2) {
LaraItem->hit_status = 1;
LaraItem->hitPoints -= 100;
DoLotsOfBlood(LaraItem->pos.x, item->pos.y - 256, LaraItem->pos.z, LaraItem->speed, LaraItem->pos.rotY, LaraItem->roomNumber, 2);
}
AnimateItem(item);
}
void __cdecl InitialiseKillerStatue(__int16 itemID) {
ITEM_INFO *item;
item = &Items[itemID];
item->animNumber = Objects[item->objectID].animIndex + 3;
item->currentAnimState = 1;
item->frameNumber = Anims[item->animNumber].frameBase;
}
void __cdecl KillerStatueControl(__int16 itemID) {
ITEM_INFO *item;
item = &Items[itemID];
if (TriggerActive(item) && item->currentAnimState == 1) {
item->goalAnimState = 2;
} else {
item->goalAnimState = 1;
}
if (CHK_ANY(item->touchBits, 0x80) && item->currentAnimState == 2) {
LaraItem->hit_status = 1;
LaraItem->hitPoints -= 20;
DoBloodSplat(LaraItem->pos.x + (GetRandomControl() - 16384) / 256,
LaraItem->pos.y - GetRandomControl() / 44,
LaraItem->pos.z + (GetRandomControl() - 16384) / 256,
LaraItem->speed,
LaraItem->pos.rotY + (GetRandomControl() - 16384) / 8,
LaraItem->roomNumber);
}
AnimateItem(item);
}
void __cdecl Pendulum(__int16 itemID) {
ITEM_INFO *item;
item = &Items[itemID];
if (item->touchBits) {
LaraItem->hitPoints -= 50;
LaraItem->hit_status = 1;
DoBloodSplat(LaraItem->pos.x + (GetRandomControl() - 16384) / 256,
LaraItem->pos.y - GetRandomControl() / 44,
LaraItem->pos.z + (GetRandomControl() - 16384) / 256,
LaraItem->speed,
LaraItem->pos.rotY + (GetRandomControl() - 16384) / 8,
LaraItem->roomNumber);
}
item->floor = GetHeight(GetFloor(item->pos.x, item->pos.y, item->pos.z, &item->roomNumber), item->pos.x, item->pos.y, item->pos.z);
AnimateItem(item);
}
void __cdecl TeethTrap(__int16 itemID) {
ITEM_INFO *item;
static BITE_INFO Teeth[3][2] = {
{{-23, 0, -1718, 0}, {71, 0, -1718, 1}},
{{-23, 10, -1718, 0}, {71, 10, -1718, 1}},
{{-23, -10, -1718, 0}, {71, -10, -1718, 1}}
};
item = &Items[itemID];
if (TriggerActive(item)) {
item->goalAnimState = 1;
if (item->touchBits && item->currentAnimState == 1) {
LaraItem->hitPoints -= 400;
LaraItem->hit_status = 1;
BaddieBiteEffect(item, &Teeth[0][0]);
BaddieBiteEffect(item, &Teeth[0][1]);
BaddieBiteEffect(item, &Teeth[1][0]);
BaddieBiteEffect(item, &Teeth[1][1]);
BaddieBiteEffect(item, &Teeth[2][0]);
BaddieBiteEffect(item, &Teeth[2][1]);
}
} else {
item->goalAnimState = 0;
}
AnimateItem(item);
}
void __cdecl FallingCeiling(__int16 itemID) {
ITEM_INFO *item;
__int16 roomID;
item = &Items[itemID];
if (!item->currentAnimState) {
item->gravity = 1;
item->goalAnimState = 1;
} else {
if (item->currentAnimState == 1 && item->touchBits) {
LaraItem->hitPoints -= 300;
LaraItem->hit_status = 1;
}
}
AnimateItem(item);
if (item->status == ITEM_DISABLED) {
RemoveActiveItem(itemID);
} else {
roomID = item->roomNumber;
item->floor = GetHeight(GetFloor(item->pos.x, item->pos.y, item->pos.z, &roomID), item->pos.x, item->pos.y, item->pos.z);
if (roomID != item->roomNumber)
ItemNewRoom(itemID, roomID);
if (item->currentAnimState == 1 && item->pos.y >= item->floor) {
item->gravity = 0;
item->goalAnimState = 2;
item->pos.y = item->floor;
item->fallSpeed = 0;
}
}
}
void __cdecl DartEmitterControl(__int16 itemID) {
ITEM_INFO *item, *dynamic;
__int16 dynamicID;
int dx, dz;
item = &Items[itemID];
if (TriggerActive(item)) {
if (!item->currentAnimState)
item->goalAnimState = 1;
} else {
if (item->currentAnimState == 1)
item->goalAnimState = 0;
}
if (item->currentAnimState == 1 && item->frameNumber == Anims[item->animNumber].frameBase) {
dynamicID = CreateItem();
if (dynamicID != -1) {
dynamic = &Items[dynamicID];
dynamic->objectID = ID_DARTS;
dynamic->roomNumber = item->roomNumber;
dynamic->shade1 = -1;
dynamic->pos.rotY = item->pos.rotY;
dynamic->pos.y = item->pos.y - 512;
dz = 0;
dx = 0;
if (dynamic->pos.rotY <= -PHD_90) {
if (dynamic->pos.rotY == -PHD_90) {
dx = 412;
} else {
if (dynamic->pos.rotY == -PHD_180)
dz = 412;
}
} else {
if (!dynamic->pos.rotY) {
dz = -412;
} else {
if (dynamic->pos.rotY == PHD_90)
dx = -412;
}
}
dynamic->pos.x = item->pos.x + dx;
dynamic->pos.z = item->pos.z + dz;
InitialiseItem(dynamicID);
AddActiveItem(dynamicID);
dynamic->status = ITEM_ACTIVE;
PlaySoundEffect(254, &dynamic->pos, 0);
}
}
AnimateItem(item);
}
void __cdecl DartsControl(__int16 itemID) {
ITEM_INFO *item;
__int16 roomID, fxID;
FLOOR_INFO *floor;
FX_INFO *fx;
item = &Items[itemID];
if (item->touchBits) {
LaraItem->hitPoints -= 50;
LaraItem->hit_status = 1;
DoBloodSplat(item->pos.x, item->pos.y, item->pos.z, LaraItem->speed, LaraItem->pos.rotY, LaraItem->roomNumber);
}
AnimateItem(item);
roomID = item->roomNumber;
floor = GetFloor(item->pos.x, item->pos.y, item->pos.z, &roomID);
if (item->roomNumber != roomID)
ItemNewRoom(itemID, roomID);
item->floor = GetHeight(floor, item->pos.x, item->pos.y, item->pos.z);
item->pos.rotX += PHD_45 / 2;
if (item->pos.y >= item->floor) {
KillItem(itemID);
fxID = CreateEffect(item->roomNumber);
if (fxID != -1) {
fx = &Effects[fxID];
fx->pos = item->pos;
fx->speed = 0;
fx->counter = 6;
fx->object_number = ID_RICOCHET;
fx->frame_number = -3 * GetRandomControl() / 32768;
}
PlaySoundEffect(258, &item->pos, 0);
}
}
void __cdecl DartEffectControl(__int16 fxID) {
FX_INFO *fx;
fx = &Effects[fxID];
++fx->counter;
if (fx->counter >= 3) {
--fx->frame_number;
fx->counter = 0;
if (fx->frame_number <= Objects[fx->object_number].nMeshes)
KillEffect(fxID);
}
}
void __cdecl FlameEmitterControl(__int16 item_id) {
ITEM_INFO *item;
__int16 fxID;
FX_INFO *fx;
item = &Items[item_id];
if (TriggerActive(item)) {
if (!item->data) {
fxID = CreateEffect(item->roomNumber);
if (fxID != -1) {
fx = &Effects[fxID];
fx->pos.x = item->pos.x;
fx->pos.y = item->pos.y;
fx->pos.z = item->pos.z;
fx->frame_number = 0;
fx->object_number = ID_FLAME;
fx->counter = 0;
}
item->data = (LPVOID) (fxID + 1);
}
} else {
if (item->data) {
KillEffect((int) item->data - 1);
item->data = (LPVOID) 0;
}
}
}
void __cdecl FlameControl(__int16 fx_id) {
FX_INFO *fx = &Effects[fx_id];
if( --fx->frame_number <= Objects[ID_FLAME].nMeshes ) {
fx->frame_number = 0;
}
if( fx->counter < 0 ) {
#ifdef FEATURE_CHEAT
if( Lara.water_status == LWS_Cheat ) {
fx->counter = 0;
KillEffect(fx_id);
Lara.burn = 0;
return;
}
#endif // FEATURE_CHEAT
fx->pos.x = 0;
fx->pos.y = 0;
fx->pos.z = ( fx->counter == -1 ) ? -100 : 0;
GetJointAbsPosition(LaraItem, (PHD_VECTOR *)&fx->pos, -1 - fx->counter);
if( LaraItem->roomNumber != fx->room_number ) {
EffectNewRoom(fx_id, LaraItem->roomNumber);
}
int height = GetWaterHeight(fx->pos.x, fx->pos.y, fx->pos.z, fx->room_number);
if( height != NO_HEIGHT && fx->pos.y > height ) {
fx->counter = 0;
KillEffect(fx_id);
Lara.burn = 0;
} else {
PlaySoundEffect(150, &fx->pos, 0);
LaraItem->hitPoints -= 7;
LaraItem->hit_status = 1;
}
} else {
PlaySoundEffect(150, &fx->pos, 0);
if( fx->counter ) {
--fx->counter;
} else if( ItemNearLara(&fx->pos, 600) ) {
LaraItem->hitPoints -= 5;
LaraItem->hit_status = 1;
int dx = LaraItem->pos.x - fx->pos.x;
int dz = LaraItem->pos.z - fx->pos.z;
if( SQR(dx) + SQR(dz) < SQR(450) ) {
fx->counter = 100;
LaraBurn();
}
}
}
}
void __cdecl LaraBurn() {
#ifdef FEATURE_CHEAT
if( Lara.water_status == LWS_Cheat ) {
return;
}
#endif // FEATURE_CHEAT
if( Lara.burn ) {
return;
}
__int16 fx_id = CreateEffect(LaraItem->roomNumber);
if( fx_id < 0 ) {
return;
}
FX_INFO *fx = &Effects[fx_id];
fx->object_number = ID_FLAME;
fx->frame_number = 0;
fx->counter = -1;
Lara.burn = 1;
}
void __cdecl LavaBurn(ITEM_INFO *item) {
#ifdef FEATURE_CHEAT
if( Lara.water_status == LWS_Cheat ) {
return;
}
#endif // FEATURE_CHEAT
if( item->hitPoints < 0 ) {
return;
}
__int16 room_number = item->roomNumber;
FLOOR_INFO *floor = GetFloor(item->pos.x, 32000, item->pos.z, &room_number);
if( item->floor != GetHeight(floor, item->pos.x, 32000, item->pos.z) ) {
return;
}
item->hit_status = 1;
item->hitPoints = -1;
for( int i = 0; i < 10; ++i ) {
__int16 fx_id = CreateEffect(item->roomNumber);
if( fx_id < 0 ) continue;
FX_INFO *fx = &Effects[fx_id];
fx->object_number = ID_FLAME;
fx->frame_number = Objects[ID_FLAME].nMeshes * GetRandomControl() / 0x7FFF;
fx->counter = -1 - 24 * GetRandomControl() / 0x7FFF;
}
}
/*
* Inject function
*/
void Inject_Traps() {
INJECT(0x00440FC0, MineControl);
INJECT(0x004411C0, ControlSpikeWall);
INJECT(0x00441300, ControlCeilingSpikes);
INJECT(0x00441420, HookControl);
// INJECT(0x004414B0, PropellerControl);
INJECT(0x00441640, SpinningBlade);
INJECT(0x004417C0, IcicleControl);
INJECT(0x004418C0, InitialiseBlade);
INJECT(0x00441900, BladeControl);
INJECT(0x004419A0, InitialiseKillerStatue);
INJECT(0x004419F0, KillerStatueControl);
// INJECT(0x00441B00, SpringBoardControl);
// INJECT(0x00441BE0, InitialiseRollingBall);
// INJECT(0x00441C20, RollingBallControl);
// INJECT(0x00441F70, RollingBallCollision);
// INJECT(0x004421C0, SpikeCollision);
// INJECT(0x00442320, TrapDoorControl);
// INJECT(0x00442370, TrapDoorFloor);
// INJECT(0x004423B0, TrapDoorCeiling);
// INJECT(0x004423F0, OnTrapDoor);
INJECT(0x004424A0, Pendulum);
// INJECT(0x004425B0, FallingBlock);
// INJECT(0x004426C0, FallingBlockFloor);
// INJECT(0x00442700, FallingBlockCeiling);
INJECT(0x00442750, TeethTrap);
INJECT(0x00442810, FallingCeiling);
INJECT(0x004428F0, DartEmitterControl);
INJECT(0x00442A30, DartsControl);
INJECT(0x00442B90, DartEffectControl);
INJECT(0x00442BE0, FlameEmitterControl);
INJECT(0x00442C70, FlameControl);
INJECT(0x00442DE0, LaraBurn);
INJECT(0x00442E30, LavaBurn);
// INJECT(0x00442F20, LavaSpray);
// INJECT(0x00442FF0, ControlLavaBlob);
}
================================================
FILE: game/traps.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef TRAPS_H_INCLUDED
#define TRAPS_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl MineControl(__int16 mineID); // 0x00440FC0
void __cdecl ControlSpikeWall(__int16 itemID); // 0x004411C0
void __cdecl ControlCeilingSpikes(__int16 itemID); // 0x00441300
void __cdecl HookControl(__int16 itemID); // 0x00441420
// 0x004414B0: PropellerControl
void __cdecl SpinningBlade(__int16 itemID); // 0x00441640
void __cdecl IcicleControl(__int16 itemID); // 0x004417C0
void __cdecl InitialiseBlade(__int16 itemID); // 0x004418C0
void __cdecl BladeControl(__int16 itemID); // 0x00441900
void __cdecl InitialiseKillerStatue(__int16 itemID); // 0x004419A0
void __cdecl KillerStatueControl(__int16 itemID); // 0x004419F0
// 0x00441B00: SpringBoardControl
// 0x00441BE0: InitialiseRollingBall
// 0x00441C20: RollingBallControl
// 0x00441F70: RollingBallCollision
// 0x004421C0: SpikeCollision
// 0x00442320: TrapDoorControl
// 0x00442370: TrapDoorFloor
// 0x004423B0: TrapDoorCeiling
// 0x004423F0: OnTrapDoor
void __cdecl Pendulum(__int16 itemID); // 0x004424A0
// 0x004425B0: FallingBlock
// 0x004426C0: FallingBlockFloor
// 0x00442700: FallingBlockCeiling
void __cdecl TeethTrap(__int16 itemID); // 0x00442750
void __cdecl FallingCeiling(__int16 itemID); // 0x00442810
void __cdecl DartEmitterControl(__int16 itemID); // 0x004428F0
void __cdecl DartsControl(__int16 itemID); // 0x00442A30
void __cdecl DartEffectControl(__int16 fxID); // 0x00442B90
void __cdecl FlameEmitterControl(__int16 item_id); // 0x00442BE0
void __cdecl FlameControl(__int16 fx_id); // 0x00442C70
void __cdecl LaraBurn(); // 0x00442DE0
void __cdecl LavaBurn(ITEM_INFO *item); // 0x00442E30
// 0x00442F20: LavaSpray
// 0x00442FF0: ControlLavaBlob
#endif // TRAPS_H_INCLUDED
================================================
FILE: game/wolf.cpp
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/wolf.h"
#include "game/box.h"
#include "game/effects.h"
#include "game/lot.h"
#include "specific/game.h"
#include "global/vars.h"
#define WOLF_TOUCH (0x774F)
typedef enum {
WOLF_EMPTY,
WOLF_STOP,
WOLF_WALK,
WOLF_RUN,
WOLF_JUMP,
WOLF_STALK,
WOLF_ATTACK,
WOLF_HOWL,
WOLF_SLEEP,
WOLF_CROUCH,
WOLF_FASTTURN,
WOLF_DEATH,
WOLF_BITE
} WOLF_ANIMS;
static const BITE_INFO WolfBite = {
0, -14, 174, 6
};
void __cdecl InitialiseWolf(__int16 itemID) {
Items[itemID].frameNumber = 96; // lying wolf frame (hardcoded thing)
InitialiseCreature(itemID);
}
void __cdecl WolfControl(__int16 itemID) {
ITEM_INFO *item = &Items[itemID];
if( item->status == ITEM_INVISIBLE ) {
if( !EnableBaddieAI(itemID, FALSE) ) {
return;
}
item->status = ITEM_ACTIVE;
}
CREATURE_INFO *wolf = (CREATURE_INFO *)item->data;
if( wolf == NULL ) return; // NOTE: additional check not presented in the original game
__int16 angle = 0;
__int16 head = 0;
__int16 tilt = 0;
if( item->hitPoints <= 0 ) {
if( item->currentAnimState != WOLF_DEATH ) {
// some hardcoded death animation start
item->animNumber = Objects[ID_SPIDER_or_WOLF].animIndex + GetRandomControl() / 11000 + 20;
item->frameNumber = Anims[item->animNumber].frameBase;
item->currentAnimState = WOLF_DEATH;
}
} else {
AI_INFO info;
CreatureAIInfo(item, &info);
if( info.ahead ) {
head = info.angle;
}
CreatureMood(item, &info, FALSE);
angle = CreatureTurn(item, wolf->maximum_turn);
switch ( item->currentAnimState ) {
case WOLF_SLEEP :
head = 0;
if( wolf->mood == MOOD_ESCAPE || info.zone_number == info.enemy_zone ) {
item->requiredAnimState = WOLF_CROUCH;
item->goalAnimState = WOLF_STOP;
} else if( GetRandomControl() < 0x20 ) {
item->requiredAnimState = WOLF_WALK;
item->goalAnimState = WOLF_STOP;
}
break;
case WOLF_STOP :
if( item->requiredAnimState != WOLF_EMPTY ) {
item->goalAnimState = item->requiredAnimState;
} else {
item->goalAnimState = WOLF_WALK;
}
break;
case WOLF_WALK :
wolf->maximum_turn = 2*PHD_DEGREE;
if( wolf->mood != MOOD_BORED ) {
item->goalAnimState = WOLF_STALK;
item->requiredAnimState = WOLF_EMPTY;
} else if( GetRandomControl() < 0x20 ) {
item->requiredAnimState = WOLF_SLEEP;
item->goalAnimState = WOLF_STOP;
}
break;
case WOLF_CROUCH :
if( item->requiredAnimState != WOLF_EMPTY ) {
item->goalAnimState = item->requiredAnimState;
} else if( wolf->mood == MOOD_ESCAPE ) {
item->goalAnimState = WOLF_RUN;
} else if( info.distance < 0x1D0F1 && info.bite ) {
item->goalAnimState = WOLF_BITE;
} else if( wolf->mood == MOOD_STALK ) {
item->goalAnimState = WOLF_STALK;
} else if( wolf->mood == MOOD_BORED ) {
item->goalAnimState = WOLF_STOP;
} else {
item->goalAnimState = WOLF_RUN;
}
break;
case WOLF_STALK :
wolf->maximum_turn = 2*PHD_DEGREE;
if( wolf->mood == MOOD_ESCAPE ) {
item->goalAnimState = WOLF_RUN;
} else if( info.distance < 0x1D0F1 && info.bite ) {
item->goalAnimState = WOLF_BITE;
} else if( info.distance > 0x900000 ) {
item->goalAnimState = WOLF_RUN;
} else if( wolf->mood == MOOD_ATTACK ) {
if( !info.ahead || info.distance > 0x240000 ||
(info.enemy_facing < 0x4000 && info.enemy_facing > -0x4000) )
{
item->goalAnimState = WOLF_RUN;
}
} else if( GetRandomControl() < 0x180 ) {
item->requiredAnimState = WOLF_HOWL;
item->goalAnimState = WOLF_CROUCH;
}
else if( wolf->mood == MOOD_BORED ) {
item->goalAnimState = WOLF_CROUCH;
}
break;
case WOLF_RUN :
wolf->maximum_turn = 5*PHD_DEGREE;
tilt = angle;
if( info.ahead && info.distance < 0x240000 ) {
if( info.distance > 0x120000 &&
(info.enemy_facing > 0x4000 || info.enemy_facing < -0x4000) )
{
item->requiredAnimState = WOLF_STALK;
item->goalAnimState = WOLF_CROUCH;
} else {
item->goalAnimState = WOLF_ATTACK;
item->requiredAnimState = WOLF_EMPTY;
}
} else if( wolf->mood == MOOD_STALK && info.distance < 0x900000 ) {
item->requiredAnimState = WOLF_STALK;
item->goalAnimState = WOLF_CROUCH;
} else if( wolf->mood == MOOD_BORED ) {
item->goalAnimState = WOLF_CROUCH;
}
break;
case WOLF_ATTACK :
tilt = angle;
if( item->requiredAnimState == WOLF_EMPTY &&
CHK_ANY(item->touchBits, WOLF_TOUCH) )
{
CreatureEffect(item, &WolfBite, DoBloodSplat);
LaraItem->hitPoints -= 50;
LaraItem->hit_status = 1;
item->requiredAnimState = WOLF_RUN;
}
item->goalAnimState = WOLF_RUN;
break;
case WOLF_BITE :
if( info.ahead &&
item->requiredAnimState == WOLF_EMPTY &&
CHK_ANY(item->touchBits, WOLF_TOUCH) )
{
CreatureEffect(item, &WolfBite, DoBloodSplat);
LaraItem->hitPoints -= 100;
LaraItem->hit_status = 1;
item->requiredAnimState = WOLF_CROUCH;
}
break;
default :
break;
}
}
CreatureTilt(item, tilt);
CreatureHead(item, head);
CreatureAnimation(itemID, angle, tilt);
}
/*
* NOTE: there is no inject function for tomb2gold code
*/
================================================
FILE: game/wolf.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef WOLF_H_INCLUDED
#define WOLF_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// NOTE: these functions are presented in the "Golden Mask" only
void __cdecl InitialiseWolf(__int16 itemID); // tomb2gold:0x004431C0
void __cdecl WolfControl(__int16 itemID); // tomb2gold:0x004431F0
#endif // WOLF_H_INCLUDED
================================================
FILE: game/yeti.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/yeti.h"
#include "global/vars.h"
/*
* Inject function
*/
void Inject_Yeti() {
// INJECT(0x00443100, GiantYetiControl);
// INJECT(0x00443400, YetiControl);
}
================================================
FILE: game/yeti.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef YETI_H_INCLUDED
#define YETI_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#define GiantYetiControl ((void(__cdecl*)(__int16)) 0x00443100)
#define YetiControl ((void(__cdecl*)(__int16)) 0x00443400)
#endif // YETI_H_INCLUDED
================================================
FILE: global/md5.c
================================================
/*
**********************************************************************
** md5.c **
** RSA Data Security, Inc. MD5 Message Digest Algorithm **
** Created: 2/17/90 RLR **
** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version **
**********************************************************************
*/
/*
**********************************************************************
** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
** **
** License to copy and use this software is granted provided that **
** it is identified as the "RSA Data Security, Inc. MD5 Message **
** Digest Algorithm" in all material mentioning or referencing this **
** software or this function. **
** **
** License is also granted to make and use derivative works **
** provided that such works are identified as "derived from the RSA **
** Data Security, Inc. MD5 Message Digest Algorithm" in all **
** material mentioning or referencing the derived work. **
** **
** RSA Data Security, Inc. makes no representations concerning **
** either the merchantability of this software or the suitability **
** of this software for any particular purpose. It is provided "as **
** is" without express or implied warranty of any kind. **
** **
** These notices must be retained in any copies of any part of this **
** documentation and/or software. **
**********************************************************************
*/
#include "md5.h"
/* forward declaration */
static void Transform (UINT4 *buf, UINT4 *in);
static unsigned char PADDING[64] = {
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/* F, G and H are basic MD5 functions: selection, majority, parity */
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
/* ROTATE_LEFT rotates x left n bits */
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
/* Rotation is separate from addition to prevent recomputation */
#define FF(a, b, c, d, x, s, ac) \
{(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) \
{(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) \
{(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) \
{(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
void MD5Init (MD5_CTX *mdContext)
{
mdContext->i[0] = mdContext->i[1] = (UINT4)0;
/* Load magic initialization constants.
*/
mdContext->buf[0] = (UINT4)0x67452301;
mdContext->buf[1] = (UINT4)0xefcdab89;
mdContext->buf[2] = (UINT4)0x98badcfe;
mdContext->buf[3] = (UINT4)0x10325476;
}
void MD5Update (MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen)
{
UINT4 in[16];
int mdi;
unsigned int i, ii;
/* compute number of bytes mod 64 */
mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
/* update number of bits */
if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])
mdContext->i[1]++;
mdContext->i[0] += ((UINT4)inLen << 3);
mdContext->i[1] += ((UINT4)inLen >> 29);
while (inLen--) {
/* add new character to buffer, increment mdi */
mdContext->in[mdi++] = *inBuf++;
/* transform if necessary */
if (mdi == 0x40) {
for (i = 0, ii = 0; i < 16; i++, ii += 4)
in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
(((UINT4)mdContext->in[ii+2]) << 16) |
(((UINT4)mdContext->in[ii+1]) << 8) |
((UINT4)mdContext->in[ii]);
Transform (mdContext->buf, in);
mdi = 0;
}
}
}
void MD5Final (MD5_CTX *mdContext)
{
UINT4 in[16];
int mdi;
unsigned int i, ii;
unsigned int padLen;
/* save number of bits */
in[14] = mdContext->i[0];
in[15] = mdContext->i[1];
/* compute number of bytes mod 64 */
mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
/* pad out to 56 mod 64 */
padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
MD5Update (mdContext, PADDING, padLen);
/* append length in bits and transform */
for (i = 0, ii = 0; i < 14; i++, ii += 4)
in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
(((UINT4)mdContext->in[ii+2]) << 16) |
(((UINT4)mdContext->in[ii+1]) << 8) |
((UINT4)mdContext->in[ii]);
Transform (mdContext->buf, in);
/* store buffer in digest */
for (i = 0, ii = 0; i < 4; i++, ii += 4) {
mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
mdContext->digest[ii+1] =
(unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
mdContext->digest[ii+2] =
(unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
mdContext->digest[ii+3] =
(unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
}
}
/* Basic MD5 step. Transform buf based on in.
*/
static void Transform (UINT4 *buf, UINT4 *in)
{
UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
/* Round 1 */
#define S11 7
#define S12 12
#define S13 17
#define S14 22
FF ( a, b, c, d, in[ 0], S11, 3614090360); /* 1 */
FF ( d, a, b, c, in[ 1], S12, 3905402710); /* 2 */
FF ( c, d, a, b, in[ 2], S13, 606105819); /* 3 */
FF ( b, c, d, a, in[ 3], S14, 3250441966); /* 4 */
FF ( a, b, c, d, in[ 4], S11, 4118548399); /* 5 */
FF ( d, a, b, c, in[ 5], S12, 1200080426); /* 6 */
FF ( c, d, a, b, in[ 6], S13, 2821735955); /* 7 */
FF ( b, c, d, a, in[ 7], S14, 4249261313); /* 8 */
FF ( a, b, c, d, in[ 8], S11, 1770035416); /* 9 */
FF ( d, a, b, c, in[ 9], S12, 2336552879); /* 10 */
FF ( c, d, a, b, in[10], S13, 4294925233); /* 11 */
FF ( b, c, d, a, in[11], S14, 2304563134); /* 12 */
FF ( a, b, c, d, in[12], S11, 1804603682); /* 13 */
FF ( d, a, b, c, in[13], S12, 4254626195); /* 14 */
FF ( c, d, a, b, in[14], S13, 2792965006); /* 15 */
FF ( b, c, d, a, in[15], S14, 1236535329); /* 16 */
/* Round 2 */
#define S21 5
#define S22 9
#define S23 14
#define S24 20
GG ( a, b, c, d, in[ 1], S21, 4129170786); /* 17 */
GG ( d, a, b, c, in[ 6], S22, 3225465664); /* 18 */
GG ( c, d, a, b, in[11], S23, 643717713); /* 19 */
GG ( b, c, d, a, in[ 0], S24, 3921069994); /* 20 */
GG ( a, b, c, d, in[ 5], S21, 3593408605); /* 21 */
GG ( d, a, b, c, in[10], S22, 38016083); /* 22 */
GG ( c, d, a, b, in[15], S23, 3634488961); /* 23 */
GG ( b, c, d, a, in[ 4], S24, 3889429448); /* 24 */
GG ( a, b, c, d, in[ 9], S21, 568446438); /* 25 */
GG ( d, a, b, c, in[14], S22, 3275163606); /* 26 */
GG ( c, d, a, b, in[ 3], S23, 4107603335); /* 27 */
GG ( b, c, d, a, in[ 8], S24, 1163531501); /* 28 */
GG ( a, b, c, d, in[13], S21, 2850285829); /* 29 */
GG ( d, a, b, c, in[ 2], S22, 4243563512); /* 30 */
GG ( c, d, a, b, in[ 7], S23, 1735328473); /* 31 */
GG ( b, c, d, a, in[12], S24, 2368359562); /* 32 */
/* Round 3 */
#define S31 4
#define S32 11
#define S33 16
#define S34 23
HH ( a, b, c, d, in[ 5], S31, 4294588738); /* 33 */
HH ( d, a, b, c, in[ 8], S32, 2272392833); /* 34 */
HH ( c, d, a, b, in[11], S33, 1839030562); /* 35 */
HH ( b, c, d, a, in[14], S34, 4259657740); /* 36 */
HH ( a, b, c, d, in[ 1], S31, 2763975236); /* 37 */
HH ( d, a, b, c, in[ 4], S32, 1272893353); /* 38 */
HH ( c, d, a, b, in[ 7], S33, 4139469664); /* 39 */
HH ( b, c, d, a, in[10], S34, 3200236656); /* 40 */
HH ( a, b, c, d, in[13], S31, 681279174); /* 41 */
HH ( d, a, b, c, in[ 0], S32, 3936430074); /* 42 */
HH ( c, d, a, b, in[ 3], S33, 3572445317); /* 43 */
HH ( b, c, d, a, in[ 6], S34, 76029189); /* 44 */
HH ( a, b, c, d, in[ 9], S31, 3654602809); /* 45 */
HH ( d, a, b, c, in[12], S32, 3873151461); /* 46 */
HH ( c, d, a, b, in[15], S33, 530742520); /* 47 */
HH ( b, c, d, a, in[ 2], S34, 3299628645); /* 48 */
/* Round 4 */
#define S41 6
#define S42 10
#define S43 15
#define S44 21
II ( a, b, c, d, in[ 0], S41, 4096336452); /* 49 */
II ( d, a, b, c, in[ 7], S42, 1126891415); /* 50 */
II ( c, d, a, b, in[14], S43, 2878612391); /* 51 */
II ( b, c, d, a, in[ 5], S44, 4237533241); /* 52 */
II ( a, b, c, d, in[12], S41, 1700485571); /* 53 */
II ( d, a, b, c, in[ 3], S42, 2399980690); /* 54 */
II ( c, d, a, b, in[10], S43, 4293915773); /* 55 */
II ( b, c, d, a, in[ 1], S44, 2240044497); /* 56 */
II ( a, b, c, d, in[ 8], S41, 1873313359); /* 57 */
II ( d, a, b, c, in[15], S42, 4264355552); /* 58 */
II ( c, d, a, b, in[ 6], S43, 2734768916); /* 59 */
II ( b, c, d, a, in[13], S44, 1309151649); /* 60 */
II ( a, b, c, d, in[ 4], S41, 4149444226); /* 61 */
II ( d, a, b, c, in[11], S42, 3174756917); /* 62 */
II ( c, d, a, b, in[ 2], S43, 718787259); /* 63 */
II ( b, c, d, a, in[ 9], S44, 3951481745); /* 64 */
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
/*
**********************************************************************
** End of md5.c **
******************************* (cut) ********************************
*/
================================================
FILE: global/md5.h
================================================
/*
**********************************************************************
** md5.h -- Header file for implementation of MD5 **
** RSA Data Security, Inc. MD5 Message Digest Algorithm **
** Created: 2/17/90 RLR **
** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version **
** Revised (for MD5): RLR 4/27/91 **
** -- G modified to have y&~z instead of y&z **
** -- FF, GG, HH modified to add in last register done **
** -- Access pattern: round 2 works mod 5, round 3 works mod 3 **
** -- distinct additive constant for each step **
** -- round 4 added, working mod 7 **
**********************************************************************
*/
/*
**********************************************************************
** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
** **
** License to copy and use this software is granted provided that **
** it is identified as the "RSA Data Security, Inc. MD5 Message **
** Digest Algorithm" in all material mentioning or referencing this **
** software or this function. **
** **
** License is also granted to make and use derivative works **
** provided that such works are identified as "derived from the RSA **
** Data Security, Inc. MD5 Message Digest Algorithm" in all **
** material mentioning or referencing the derived work. **
** **
** RSA Data Security, Inc. makes no representations concerning **
** either the merchantability of this software or the suitability **
** of this software for any particular purpose. It is provided "as **
** is" without express or implied warranty of any kind. **
** **
** These notices must be retained in any copies of any part of this **
** documentation and/or software. **
**********************************************************************
*/
#ifndef MD5_H_INCLUDED
#define MD5_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
/* typedef a 32 bit type */
typedef unsigned long int UINT4;
/* Data structure for MD5 (Message Digest) computation */
typedef struct {
UINT4 i[2]; /* number of _bits_ handled mod 2^64 */
UINT4 buf[4]; /* scratch buffer */
unsigned char in[64]; /* input buffer */
unsigned char digest[16]; /* actual digest after MD5Final call */
} MD5_CTX;
void MD5Init (MD5_CTX *mdContext);
void MD5Update (MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen);
void MD5Final (MD5_CTX *mdContext);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif // MD5_H_INCLUDED
/*
**********************************************************************
** End of md5.h **
******************************* (cut) ********************************
*/
================================================
FILE: global/memmem.c
================================================
/*-
* Copyright (c) 2005 Pascal Gloor
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include
/*
* Find the first occurrence of the byte string s in byte string l.
*/
void *
memmem(const void *l, size_t l_len, const void *s, size_t s_len)
{
register char *cur, *last;
const char *cl = (const char *)l;
const char *cs = (const char *)s;
/* we need something to compare */
if (l_len == 0 || s_len == 0)
return NULL;
/* "s" must be smaller or equal to "l" */
if (l_len < s_len)
return NULL;
/* special case where s_len == 1 */
if (s_len == 1)
return memchr(l, (int)*cs, l_len);
/* the last position where its possible to find "s" in "l" */
last = (char *)cl + l_len - s_len;
for (cur = (char *)cl; cur <= last; cur++)
if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
return cur;
return NULL;
}
================================================
FILE: global/memmem.h
================================================
/*-
* Copyright (c) 2005 Pascal Gloor
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef MEMMEM_H_INCLUDED
#define MEMMEM_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
/*
* Find the first occurrence of the byte string s in byte string l.
*/
void *memmem(const void *l, size_t l_len, const void *s, size_t s_len);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif // MEMMEM_H_INCLUDED
================================================
FILE: global/precompiled.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef TR2MAIN_PRECOMPILED_HEADER
#define TR2MAIN_PRECOMPILED_HEADER
// There is no DirectInput above 8.0
#if (DIRECTINPUT_VERSION > 0x800)
#undef DIRECTINPUT_VERSION
#define DIRECTINPUT_VERSION (0x800)
#endif // (DIRECTINPUT_VERSION > 0x800)
// There is no DirectSound above 9.0
#if (DIRECTSOUND_VERSION > 0x900)
#undef DIRECTSOUND_VERSION
#define DIRECTSOUND_VERSION (0x900)
#endif // (DIRECTSOUND_VERSION > 0x900)
// Some defines are required for DX9 and above
#if (DIRECT3D_VERSION >= 0x900)
#ifndef FEATURE_NOLEGACY_OPTIONS
#define FEATURE_NOLEGACY_OPTIONS
#endif // !FEATURE_NOLEGACY_OPTIONS
#endif // (DIRECT3D_VERSION >= 0x900)
#include
#include
#include
#include
#include
#include
#if (DIRECT3D_VERSION >= 0x900)
#include
#include
#else // (DIRECT3D_VERSION >= 0x900)
#include
#include
#endif // (DIRECT3D_VERSION >= 0x900)
#endif // TR2MAIN_PRECOMPILED_HEADER
================================================
FILE: global/resource.h
================================================
/*
* Copyright (c) 2017 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef RESOURCE_H_INCLUDED
#define RESOURCE_H_INCLUDED
#define RESOURCE_H_INCLUDED
// String table
#define IDS_DX5_REQUIRED 1
// Icon groups
#define IDI_MAINICON 100
#define IDI_ICON_01 133
#define IDI_ICON_02 146
#define IDI_ICON_03 147
#define IDI_ICON_04 148
#define IDI_ICON_05 149
#define IDI_ICON_06 150
#define IDI_ICON_07 151
#define IDI_ICON_08 152
#define IDI_ICON_09 153
#define IDI_ICON_10 154
#define IDI_ICON_11 155
#define IDI_ICON_12 156
// General resources
#define IDR_WAVESAMPLE 163
// Options Dialog
#define IDD_OPTIONS 110
#define ID_OPTNS_WINDOW_IMAGE 1073
#define ID_OPTNS_STATIC_GRAPHICS 1077
#define ID_OPTNS_STATIC_USING 1078
#define ID_OPTNS_STATIC_DISPLAY 1079
#define ID_OPTNS_STATIC_OPTIONS 1080
#define ID_OPTNS_STATIC_SOUND 1081
#define ID_OPTNS_STATIC_SFX 1082
#define ID_OPTNS_STATIC_JOYSTICK 1084
#define ID_OPTNS_STATIC_MIC 1085
// Graphics Dialog
#define IDD_GRAPHICS 107
#define ID_GRAPH_COMBOBOX_ADAPTER 1025
#define ID_GRAPH_BUTTON_SWRENDER 1045
#define ID_GRAPH_BUTTON_HWRENDER 1047
#define ID_GRAPH_BUTTON_ZBUFFER 1017
#define ID_GRAPH_BUTTON_BILINEAR 1018
#define ID_GRAPH_BUTTON_DITHER 1058
#define ID_GRAPH_BUTTON_TRIPLEBUFFER 1048
#define ID_GRAPH_BUTTON_PERSPECTIVE 1015
#define ID_GRAPH_BUTTON_FULLSCREEN 1030
#define ID_GRAPH_BUTTON_WINDOWED 1031
#define ID_GRAPH_COMBOBOX_FULLSCREEN 1000
#define ID_GRAPH_SLIDER_WINDOWED 1034
#define ID_GRAPH_BUTTON_ASPECT_4_3 1060
#define ID_GRAPH_BUTTON_ASPECT_16_9 1061
#define ID_GRAPH_BUTTON_ASPECT_ANY 1062
#define ID_GRAPH_BUTTON_TEST 1029
#define ID_GRAPH_GROUPBOX_ADAPTER 1041
#define ID_GRAPH_GROUPBOX_RENDER 1049
#define ID_GRAPH_GROUPBOX_OPTIONS 1051
#define ID_GRAPH_GROUPBOX_DISPLAY 1050
#define ID_GRAPH_GROUPBOX_FULLSCREEN 1038
#define ID_GRAPH_GROUPBOX_WINDOWED 1037
#define ID_GRAPH_STATIC_RES_SMALL 1039
#define ID_GRAPH_STATIC_RESOLUTION 1035
#define ID_GRAPH_STATIC_RES_LARGE 1040
#define ID_GRAPH_GROUPBOX_TEST 1044
#define ID_GRAPH_STATIC_TESTRESULT 1043
#define ID_GRAPH_STATIC_TESTDETAIL 1046
#define ID_GRAPH_BUTTON_DEFAULT 1112
// Sound Dialog
#define IDD_SOUND 108
#define ID_SOUND_COMBOBOX_ADAPTER 1052
#define ID_SOUND_BUTTON_MIC_CAMERA 1086
#define ID_SOUND_BUTTON_MIC_LARA 1087
#define ID_SOUND_BUTTON_SFX_ENABLE 1057
#define ID_SOUND_GROUPBOX_ADAPTER 1053
#define ID_SOUND_GROUPBOX_MIC 1097
#define ID_SOUND_GROUPBOX_OUTPUT 1098
#define ID_SOUND_WINDOW_IMAGE 1074
#define ID_SOUND_BUTTON_TEST 1089
// Controls Dialog
#define IDD_CONTROLS 109
#define ID_CTRLS_COMBOBOX_JOYSTICK 1055
#define ID_CTRLS_GROUPBOX_JOYSTICK 1053
#define ID_CTRLS_WINDOW_IMAGE 1075
#define ID_CTRLS_BUTTON_JOYSTICK_ENABLE 1099
#define ID_CTRLS_BUTTON_CTL_PANEL 1100
// Advanced Dialog
#define IDD_ADVANCED 144
#define ID_ADVNC_BUTTON_16BIT_DISABLE 1103
#define ID_ADVNC_BUTTON_SORT_DISABLE 1104
#define ID_ADVNC_BUTTON_ADJUST_DISABLE 1107
#define ID_ADVNC_BUTTON_ADJUST_BILINEAR 1108
#define ID_ADVNC_BUTTON_ADJUST_ALWAYS 1109
#define ID_ADVNC_WINDOW_IMAGE 1110
#define ID_ADVNC_BUTTON_FMV_DISABLE 1111
// CD Prompt Dialog
#define IDD_CD_PROMPT 165
// Demo Dialog
#define IDD_DEMO 129
#define ID_DEMO_WINDOW_IMAGE 1096
#endif // RESOURCE_H_INCLUDED
================================================
FILE: global/types.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef GLOBAL_TYPES_H_INCLUDED
#define GLOBAL_TYPES_H_INCLUDED
/*
* Inject macro
*/
#pragma pack(push, 1)
typedef struct {
BYTE opCode; // must be 0xE9;
DWORD offset; // jump offset
} JMP;
#pragma pack(pop)
#define INJECT(from,to) { \
((JMP*)(from))->opCode = 0xE9; \
((JMP*)(from))->offset = (DWORD)(to) - ((DWORD)(from) + sizeof(JMP)); \
}
#ifdef _DEBUG
#define TRACE(func,line) { \
printf("%s: line %d\n", func, line); \
fflush(stdout); \
}
#else
#define TRACE(func,line)
#endif
/*
* Defined values
*/
// General values
#define REQ_LEVEL_VERSION (45)
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
// FPS values
#define FRAMES_PER_SECOND (30)
#define TICKS_PER_FRAME (2)
#define TICKS_PER_SECOND (TICKS_PER_FRAME * FRAMES_PER_SECOND)
// Formula values
#define W2V_SHIFT (14) // World to View shift value
#define W2V_SCALE (1<(b))?(a):(b))
#define ABS(a) (((a)<0)?-(a):(a))
#define SQR(a) ((a)*(a))
#define CLAMPL(a,b) {if((a)<(b)) (a)=(b);}
#define CLAMPG(a,b) {if((a)>(b)) (a)=(b);}
#define CLAMP(a,b,c) {if((a)<(b)) (a)=(b); else if((a)>(c)) (a)=(c);}
#define SWAP(a,b,c) {(c)=(a); (a)=(b); (b)=(c);}
#define TRIGMULT2(a,b) (((a)*(b))>>W2V_SHIFT)
#define TRIGMULT3(a,b,c) (TRIGMULT2((TRIGMULT2(a,b)),c))
#define VBUF_VISIBLE(a,b,c) (((a).ys-(b).ys)*((c).xs-(b).xs)>=((c).ys-(b).ys)*((a).xs-(b).xs))
// Fast conversion macros
#define BYTEn(a,b) (*((BYTE*)&(a)+b))
#define BYTE0(a) (LOBYTE(a))
#define BYTE1(a) (BYTEn(a,1))
#define BYTE2(a) (BYTEn(a,2))
#define BYTE3(a) (BYTEn(a,3))
// View distance values
#define VIEW_NEAR (0x14 * 0x001)
#define VIEW_FAR (0x14 * 0x400)
// DepthQ fog values
#define DEPTHQ_END (VIEW_FAR)
#define DEPTHQ_RANGE (DEPTHQ_END * 2/5)
#define DEPTHQ_START (DEPTHQ_END - DEPTHQ_RANGE)
// Water effect table parameters
#define WIBBLE_SIZE (32)
#define MAX_WIBBLE (2)
#define MAX_SHADE (0x300)
#define MAX_ROOMLIGHT_UNIT (0x2000 / (WIBBLE_SIZE/2))
// SW Renderer Detail Settings
#define SW_DETAIL_LOW (0 * 0x400 * W2V_SCALE)
#define SW_DETAIL_MEDIUM (3 * 0x400 * W2V_SCALE)
#define SW_DETAIL_HIGH (6 * 0x400 * W2V_SCALE)
#define SW_DETAIL_ULTRA (20* 0x400 * W2V_SCALE)
// ClearBuffers flags
#define CLRB_PrimaryBuffer (0x0001)
#define CLRB_BackBuffer (0x0002)
#define CLRB_ThirdBuffer (0x0004)
#define CLRB_ZBuffer (0x0008)
#define CLRB_RenderBuffer (0x0010)
#define CLRB_PictureBuffer (0x0020)
#define CLRB_WindowedPrimaryBuffer (0x0040)
#define CLRB_Reserved (0x0080)
#define CLRB_PhdWinSize (0x0100)
// TextStrInfo flags
#define TIF_Active (0x0001)
#define TIF_Flash (0x0002)
#define TIF_RotateH (0x0004)
#define TIF_RotateV (0x0008)
#define TIF_CentreH (0x0010)
#define TIF_CentreV (0x0020)
#define TIF_Hide (0x0040)
#define TIF_Right (0x0080)
#define TIF_Bottom (0x0100)
#define TIF_Bgnd (0x0200)
#define TIF_Outline (0x0400)
#define TIF_Multiline (0x0800)
// GameFlow flags
#define GFF_DemoVersion (0x0001)
#define GFF_TitleDisabled (0x0002)
#define GFF_CheatModeCheckDisabled (0x0004)
#define GFF_NoInputTimeout (0x0008)
#define GFF_LoadSaveDisabled (0x0010)
#define GFF_ScreenSizingDisabled (0x0020)
#define GFF_LockoutOptionRing (0x0040)
#define GFF_DozyCheatEnabled (0x0080)
#define GFF_UseSecurityTag (0x0100)
#define GFF_GymEnabled (0x0200)
#define GFF_SelectAnyLevel (0x0400)
#define GFF_EnableCheatCode (0x0800)
// Input Status Flags
#define IN_FORWARD (0x00000001)
#define IN_BACK (0x00000002)
#define IN_LEFT (0x00000004)
#define IN_RIGHT (0x00000008)
#define IN_JUMP (0x00000010)
#define IN_DRAW (0x00000020)
#define IN_ACTION (0x00000040)
#define IN_SLOW (0x00000080)
#define IN_OPTION (0x00000100)
#define IN_LOOK (0x00000200)
#define IN_STEPL (0x00000400)
#define IN_STEPR (0x00000800)
#define IN_ROLL (0x00001000)
#define IN_PAUSE (0x00002000)
#define IN_RESERVED1 (0x00004000)
#define IN_RESERVED2 (0x00008000)
#define IN_DOZYCHEAT (0x00010000)
#define IN_STUFFCHEAT (0x00020000)
#define IN_DEBUGINFO (0x00040000)
#define IN_FLARE (0x00080000)
#define IN_SELECT (0x00100000)
#define IN_DESELECT (0x00200000)
#define IN_SAVE (0x00400000)
#define IN_LOAD (0x00800000)
// Gameflow directions
#define GF_START_GAME (0x0000)
#define GF_START_SAVEDGAME (0x0100)
#define GF_START_CINE (0x0200)
#define GF_START_FMV (0x0300)
#define GF_START_DEMO (0x0400)
#define GF_EXIT_TO_TITLE (0x0500)
#define GF_LEVEL_COMPLETE (0x0600)
#define GF_EXIT_GAME (0x0700)
#define GF_EXIT_TO_OPTION (0x0800)
#define GF_TITLE_DESELECT (0x0900)
#define GF_ERROR (-1)
// Room flags
#define ROOM_UNDERWATER (0x01)
#define ROOM_OUTSIDE (0x08)
#define ROOM_INSIDE (0x40)
// SFX flags
#define SFX_UNDERWATER (1)
#define SFX_ALWAYS (2)
// Sprite flags
#define SPR_RGB (0x00FFFFFF)
#define SPR_ABS (0x01000000)
#define SPR_SEMITRANS (0x02000000)
#define SPR_SCALE (0x04000000)
#define SPR_SHADE (0x08000000)
#define SPR_TINT (0x10000000)
#define SPR_BLEND_ADD (0x20000000)
#define SPR_BLEND_SUB (0x40000000)
#define SPR_BLEND_QRT (SPR_BLEND_ADD|SPR_BLEND_SUB)
#define SPR_BLEND (SPR_BLEND_QRT)
#define SPR_ITEM (0x80000000)
// Item flags
#define IFL_INVISIBLE (0x0100)
#define IFL_CODEBITS (0x3E00)
#define IFL_REVERSE (0x4000)
#define IFL_CLEARBODY (0x8000)
// Glow tint colors
#define GLOW_FLARE_COLOR (0xFF8080) // Flare
#define GLOW_PISTOL_COLOR (0x3F380F) // Pistol/Magnums/Uzi gunfire
#define GLOW_M16_COLOR (0x7F701F) // M16 gunfire
#define GLOW_GUNSHOT_COLOR (0x7F701F) // Skidoo/Enemy gunfire
// Collision types
#define COLL_FRONT (0x01)
#define COLL_LEFT (0x02)
#define COLL_RIGHT (0x04)
#define COLL_TOP (0x08)
/*
* DirectX type definitions
*/
#if (DIRECT3D_VERSION >= 0x900)
#ifndef RGB_MAKE
#define RGBA_SETALPHA(rgba, x) (((x) << 24) | ((rgba) & 0x00ffffff))
#define RGBA_GETALPHA(rgb) ((rgb) >> 24)
#define RGBA_GETRED(rgb) (((rgb) >> 16) & 0xff)
#define RGBA_GETGREEN(rgb) (((rgb) >> 8) & 0xff)
#define RGBA_GETBLUE(rgb) ((rgb) & 0xff)
#define RGBA_MAKE(r, g, b, a) ((D3DCOLOR) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)))
#define RGB_GETRED(rgb) (((rgb) >> 16) & 0xff)
#define RGB_GETGREEN(rgb) (((rgb) >> 8) & 0xff)
#define RGB_GETBLUE(rgb) ((rgb) & 0xff)
#define RGB_MAKE(r, g, b) ((D3DCOLOR) (((r) << 16) | ((g) << 8) | (b)))
#endif // RGB_MAKE
#ifndef D3DFVF_TLVERTEX
#define D3DFVF_TLVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_SPECULAR|D3DFVF_TEX1)
#endif // D3DFVF_TLVERTEX
typedef struct {
D3DVALUE sx, sy, sz, rhw;
D3DCOLOR color, specular;
D3DVALUE tu, tv;
} D3DTLVERTEX, *LPD3DTLVERTEX;
typedef D3DLOCKED_RECT DDSDESC, *LPDDSDESC;
typedef LPDIRECT3DSURFACE9 LPDDS;
typedef LPDIRECT3DTEXTURE9 HWR_TEXHANDLE;
#else // (DIRECT3D_VERSION >= 0x900)
typedef DDSURFACEDESC DDSDESC, *LPDDSDESC;
typedef LPDIRECTDRAWSURFACE3 LPDDS;
typedef D3DTEXTUREHANDLE HWR_TEXHANDLE;
#endif // (DIRECT3D_VERSION >= 0x900)
#if (DIRECT3D_VERSION >= 0x900)
#define VTXBUF_LEN (256)
typedef struct {
DWORD width;
DWORD height;
LPBYTE bitmap;
} SWR_BUFFER;
#endif // (DIRECT3D_VERSION >= 0x900)
/*
* Enums
*/
typedef enum {
ID_NONE = -1,
ID_LARA = 0,
ID_LARA_PISTOLS,
ID_LARA_HAIR,
ID_LARA_SHOTGUN,
ID_LARA_MAGNUMS,
ID_LARA_UZIS,
ID_LARA_M16,
ID_LARA_GRENADE,
ID_LARA_HARPOON,
ID_LARA_FLARE,
ID_LARA_SKIDOO,
ID_LARA_BOAT,
ID_LARA_EXTRA,
ID_SKIDOO_FAST,
ID_BOAT,
ID_DOG,
ID_CULT1,
ID_CULT1A,
ID_CULT1B,
ID_CULT2,
ID_CULT3,
ID_MOUSE,
ID_DRAGON_FRONT,
ID_DRAGON_BACK,
ID_GONDOLA,
ID_SHARK,
ID_EEL,
ID_BIG_EEL,
ID_BARRACUDA,
ID_DIVER,
ID_WORKER1,
ID_WORKER2,
ID_WORKER3,
ID_WORKER4,
ID_WORKER5,
ID_JELLY,
ID_SPIDER_or_WOLF,
ID_BIG_SPIDER_or_BEAR,
ID_CROW,
ID_TIGER,
ID_BARTOLI,
ID_XIAN_LORD,
ID_CHINESE2,
ID_WARRIOR,
ID_CHINESE4,
ID_YETI,
ID_GIANT_YETI,
ID_EAGLE,
ID_BANDIT1,
ID_BANDIT2,
ID_BANDIT2B,
ID_SKIDOO_ARMED,
ID_SKIDMAN,
ID_MONK1,
ID_MONK2,
ID_FALLING_BLOCK1,
ID_FALLING_BLOCK2,
ID_FALLING_BLOCK3,
ID_PENDULUM1,
ID_SPIKES,
ID_ROLLING_BALL1,
ID_DARTS,
ID_DART_EMITTER,
ID_DRAW_BRIDGE,
ID_TEETH_TRAP,
ID_LIFT,
ID_GENERAL,
ID_MOVABLE_BLOCK1,
ID_MOVABLE_BLOCK2,
ID_MOVABLE_BLOCK3,
ID_MOVABLE_BLOCK4,
ID_BIG_BOWL,
ID_WINDOW1,
ID_WINDOW2,
ID_WINDOW3,
ID_WINDOW4,
ID_PROPELLER1,
ID_PROPELLER2,
ID_HOOK,
ID_FALLING_CEILING,
ID_SPINNING_BLADE,
ID_BLADE,
ID_KILLER_STATUE,
ID_ROLLING_BALL2,
ID_ICICLE,
ID_SPIKE_WALL,
ID_SPRING_BOARD,
ID_CEILING_SPIKES,
ID_BELL,
ID_WATER_SPRITE,
ID_SNOW_SPRITE,
ID_SKIDOO_LARA,
ID_SWITCH_TYPE1,
ID_SWITCH_TYPE2,
ID_PROPELLER3,
ID_PROPELLER4,
ID_PENDULUM2,
ID_MESH_SWAP1,
ID_MESH_SWAP2,
ID_LARA_SWAP,
ID_TEXT_BOX,
ID_ROLLING_BALL3,
ID_DEATH_SLIDE,
ID_SWITCH_TYPE3,
ID_SWITCH_TYPE4,
ID_SWITCH_TYPE5,
ID_DOOR_TYPE1,
ID_DOOR_TYPE2,
ID_DOOR_TYPE3,
ID_DOOR_TYPE4,
ID_DOOR_TYPE5,
ID_DOOR_TYPE6,
ID_DOOR_TYPE7,
ID_DOOR_TYPE8,
ID_TRAPDOOR_TYPE1,
ID_TRAPDOOR_TYPE2,
ID_TRAPDOOR_TYPE3,
ID_BRIDGE_FLAT,
ID_BRIDGE_TILT1,
ID_BRIDGE_TILT2,
ID_PASSPORT_OPTION,
ID_COMPASS_OPTION,
ID_PHOTO_OPTION,
ID_PLAYER1,
ID_PLAYER2,
ID_PLAYER3,
ID_PLAYER4,
ID_PLAYER5,
ID_PLAYER6,
ID_PLAYER7,
ID_PLAYER8,
ID_PLAYER9,
ID_PLAYER10,
ID_PASSPORT_CLOSED,
ID_COMPASS_ITEM,
ID_PISTOL_ITEM,
ID_SHOTGUN_ITEM,
ID_MAGNUM_ITEM,
ID_UZI_ITEM,
ID_HARPOON_ITEM,
ID_M16_ITEM,
ID_GRENADE_ITEM,
ID_PISTOL_AMMO_ITEM,
ID_SHOTGUN_AMMO_ITEM,
ID_MAGNUM_AMMO_ITEM,
ID_UZI_AMMO_ITEM,
ID_HARPOON_AMMO_ITEM,
ID_M16_AMMO_ITEM,
ID_GRENADE_AMMO_ITEM,
ID_SMALL_MEDIPACK_ITEM,
ID_LARGE_MEDIPACK_ITEM,
ID_FLARES_ITEM,
ID_FLARE_ITEM,
ID_DETAIL_OPTION,
ID_SOUND_OPTION,
ID_CONTROL_OPTION,
ID_GAMMA_OPTION,
ID_PISTOL_OPTION,
ID_SHOTGUN_OPTION,
ID_MAGNUM_OPTION,
ID_UZI_OPTION,
ID_HARPOON_OPTION,
ID_M16_OPTION,
ID_GRENADE_OPTION,
ID_PISTOL_AMMO_OPTION,
ID_SHOTGUN_AMMO_OPTION,
ID_MAGNUM_AMMO_OPTION,
ID_UZI_AMMO_OPTION,
ID_HARPOON_AMMO_OPTION,
ID_M16_AMMO_OPTION,
ID_GRENADE_AMMO_OPTION,
ID_SMALL_MEDIPACK_OPTION,
ID_LARGE_MEDIPACK_OPTION,
ID_FLARES_OPTION,
ID_PUZZLE_ITEM1,
ID_PUZZLE_ITEM2,
ID_PUZZLE_ITEM3,
ID_PUZZLE_ITEM4,
ID_PUZZLE_OPTION1,
ID_PUZZLE_OPTION2,
ID_PUZZLE_OPTION3,
ID_PUZZLE_OPTION4,
ID_PUZZLE_HOLE1,
ID_PUZZLE_HOLE2,
ID_PUZZLE_HOLE3,
ID_PUZZLE_HOLE4,
ID_PUZZLE_DONE1,
ID_PUZZLE_DONE2,
ID_PUZZLE_DONE3,
ID_PUZZLE_DONE4,
ID_SECRET1,
ID_SECRET2,
ID_SECRET3,
ID_KEY_ITEM1,
ID_KEY_ITEM2,
ID_KEY_ITEM3,
ID_KEY_ITEM4,
ID_KEY_OPTION1,
ID_KEY_OPTION2,
ID_KEY_OPTION3,
ID_KEY_OPTION4,
ID_KEY_HOLE1,
ID_KEY_HOLE2,
ID_KEY_HOLE3,
ID_KEY_HOLE4,
ID_PICKUP_ITEM1,
ID_PICKUP_ITEM2,
ID_PICKUP_OPTION1,
ID_PICKUP_OPTION2,
ID_SPHERE_OF_DOOM1,
ID_SPHERE_OF_DOOM2,
ID_SPHERE_OF_DOOM3,
ID_ALARM_SOUND,
ID_BIRD_TWEETER1,
ID_DINO,
ID_BIRD_TWEETER2,
ID_CLOCK_CHIMES,
ID_DRAGON_BONES1,
ID_DRAGON_BONES2,
ID_DRAGON_BONES3,
ID_HOT_LIQUID,
ID_BOAT_BITS,
ID_MINE,
ID_INV_BACKGROUND,
ID_FX_RESERVED,
ID_GONG_BONGER,
ID_DETONATOR1,
ID_DETONATOR2,
ID_COPTER,
ID_EXPLOSION,
ID_SPLASH,
ID_BUBBLES,
ID_BUBBLE_EMITTER,
ID_BLOOD,
ID_DART_EFFECT,
ID_FLARE_FIRE,
ID_GLOW,
ID_GLOW_RESERVED,
ID_RICOCHET,
ID_TWINKLE,
ID_GUN_FLASH,
ID_M16_FLASH,
ID_BODY_PART,
ID_CAMERA_TARGET,
ID_WATERFALL,
ID_MISSILE_HARPOON,
ID_MISSILE_FLAME,
ID_MISSILE_KNIFE,
ID_ROCKET,
ID_HARPOON_BOLT,
ID_LAVA,
ID_LAVA_EMITTER,
ID_FLAME,
ID_FLAME_EMITTER,
ID_SKYBOX,
ID_ALPHABET,
ID_DYING_MONK,
ID_DING_DONG,
ID_LARA_ALARM,
ID_MINI_COPTER,
ID_WINSTON,
ID_ASSAULT_DIGITS,
ID_FINAL_LEVEL_COUNTER,
ID_CUT_SHOTGUN,
ID_EARTHQUAKE,
ID_NUMBER_OBJECTS,
} GAME_OBJECT_ID;
typedef enum {
AS_WALK,
AS_RUN,
AS_STOP,
AS_FORWARDJUMP,
AS_POSE,
AS_FASTBACK,
AS_TURN_R,
AS_TURN_L,
AS_DEATH,
AS_FASTFALL,
AS_HANG,
AS_REACH,
AS_SPLAT,
AS_TREAD,
AS_LAND,
AS_COMPRESS,
AS_BACK,
AS_SWIM,
AS_GLIDE,
AS_NULL,
AS_FASTTURN,
AS_STEPRIGHT,
AS_STEPLEFT,
AS_HIT,
AS_SLIDE,
AS_BACKJUMP,
AS_RIGHTJUMP,
AS_LEFTJUMP,
AS_UPJUMP,
AS_FALLBACK,
AS_HANGLEFT,
AS_HANGRIGHT,
AS_SLIDEBACK,
AS_SURFTREAD,
AS_SURFSWIM,
AS_DIVE,
AS_PUSHBLOCK,
AS_PULLBLOCK,
AS_PPREADY,
AS_PICKUP,
AS_SWITCHON,
AS_SWITCHOFF,
AS_USEKEY,
AS_USEPUZZLE,
AS_UWDEATH,
AS_ROLL,
AS_SPECIAL,
AS_SURFBACK,
AS_SURFLEFT,
AS_SURFRIGHT,
AS_USEMIDAS,
AS_DIEMIDAS,
AS_SWANDIVE,
AS_FASTDIVE,
AS_GYMNAST,
AS_WATEROUT,
AS_CLIMBSTNC,
AS_CLIMBING,
AS_CLIMBLEFT,
AS_CLIMBEND,
AS_CLIMBRIGHT,
AS_CLIMBDOWN,
AS_LARATEST1,
AS_LARATEST2,
AS_LARATEST3,
AS_WADE,
AS_WATERROLL,
AS_FLAREPICKUP,
AS_TWIST,
AS_KICK,
AS_DEATHSLIDE,
AS_LAST,
} LARA_ANIM_STATES;
typedef enum {
EXTRA_BREATH,
EXTRA_PLUNGER,
EXTRA_YETIKILL,
EXTRA_SHARKKILL,
EXTRA_AIRLOCK,
EXTRA_GONGBONG,
EXTRA_DINOKILL,
EXTRA_PULLDAGGER,
EXTRA_STARTANIM,
EXTRA_STARTHOUSE,
EXTRA_FINALANIM,
} LARA_EXTRA_STATES;
typedef enum {
VGA_NoVga,
VGA_256Color,
VGA_ModeX,
VGA_Standard,
} VGA_MODE;
typedef enum {
RM_Unknown,
RM_Software,
RM_Hardware,
} RENDER_MODE;
typedef enum {
AM_4_3,
AM_16_9,
AM_Any,
} ASPECT_MODE;
typedef enum {
TAM_Disabled,
TAM_BilinearOnly,
TAM_Always,
} TEX_ADJUST_MODE;
#ifdef FEATURE_INPUT_IMPROVED
typedef enum {
JOY_DirectInput,
JOY_XInput,
JOY_RawInput,
} JOY_INTERFACE;
#endif // FEATURE_INPUT_IMPROVED
typedef enum {
#ifdef FEATURE_HUD_IMPROVED
CTRL_Joystick,
CTRL_Custom,
CTRL_Default,
#else // FEATURE_HUD_IMPROVED
CTRL_Default,
CTRL_Custom,
#endif // FEATURE_HUD_IMPROVED
} CONTROL_LAYOUT_PAGE;
typedef enum {
INV_GameMode,
INV_TitleMode,
INV_KeysMode,
INV_SaveMode,
INV_LoadMode,
INV_DeathMode,
INV_PauseMode, // NOTE: not presented in the original game
} INVENTORY_MODE;
typedef enum {
LGS_Armless,
LGS_HandBusy,
LGS_Draw,
LGS_Undraw,
LGS_Ready,
LGS_Special,
} LARA_GUN_STATUS;
typedef enum {
LGT_Unarmed,
LGT_Pistols,
LGT_Magnums,
LGT_Uzis,
LGT_Shotgun,
LGT_M16,
LGT_Grenade,
LGT_Harpoon,
LGT_Flare,
LGT_Skidoo,
} LARA_GUN_TYPE;
typedef enum {
CAM_Chase,
CAM_Fixed,
CAM_Look,
CAM_Combat,
CAM_Cinematic,
CAM_Heavy,
} CAMERA_TYPE;
typedef enum {
CFL_None,
CFL_FollowCenter,
CFL_NoChunky,
CFL_ChaseObject,
} CAMERA_FLAG;
typedef enum {
KM_Forward,
KM_Back,
KM_Left,
KM_Right,
KM_StepLeft,
KM_StepRight,
#ifdef FEATURE_HUD_IMPROVED
KM_Step,
#endif // FEATURE_HUD_IMPROVED
KM_Slow,
KM_Jump,
KM_Action,
#ifdef FEATURE_HUD_IMPROVED
KM_Roll,
KM_WeaponDraw,
KM_Flare,
KM_Look,
KM_Option,
KM_Pause,
#else // FEATURE_HUD_IMPROVED
KM_WeaponDraw,
KM_Flare,
KM_Look,
KM_Roll,
KM_Option,
#endif // FEATURE_HUD_IMPROVED
} KEYMAP;
typedef enum {
ST_AvgZ,
ST_MaxZ,
ST_FarZ,
} SORTTYPE;
typedef enum {
DRAW_Opaque,
DRAW_ColorKey,
DRAW_Semitrans, // NOTE: semitrans is not presented in the original code
} DRAWTYPE;
typedef enum {
// Software renderer
POLY_GTmap, // gouraud shaded poly (texture)
POLY_WGTmap, // gouraud shaded poly (texture + colorkey)
POLY_GTmap_persp, // gouraud shaded poly (texture + perspective)
POLY_WGTmap_persp, // gouraud shaded poly (texture + colorkey + perspective)
POLY_line, // line (color)
POLY_flat, // flat shaded poly (color)
POLY_gouraud, // gouraud shaded poly (color)
POLY_trans, // shadow poly (color + semitransparent)
POLY_sprite, // scaled sprite (texture + colorkey)
// Hardware renderer
POLY_HWR_GTmap, // gouraud shaded poly (texture)
POLY_HWR_WGTmap, // gouraud shaded poly (texture + colorkey)
POLY_HWR_gouraud, // gouraud shaded poly (color)
POLY_HWR_line, // line (color)
POLY_HWR_trans, // shadow poly (color + semitransparent)
#ifdef FEATURE_VIDEOFX_IMPROVED
POLY_HWR_WGTmapHalf,// semitransparent gouraud shaded poly (texture + colorkey)
POLY_HWR_WGTmapAdd, // additive blended gouraud shaded poly (texture + colorkey)
POLY_HWR_WGTmapSub, // subtractive blended gouraud shaded poly (texture + colorkey)
POLY_HWR_WGTmapQrt, // quarter blended gouraud shaded poly (texture + colorkey)
POLY_HWR_half, // semitransparent gouraud shaded poly (color)
POLY_HWR_add, // additive blended gouraud shaded poly (color)
POLY_HWR_sub, // subtractive blended gouraud shaded poly (color)
POLY_HWR_qrt, // quarter blended gouraud shaded poly (color)
#endif // FEATURE_VIDEOFX_IMPROVED
#ifdef FEATURE_HUD_IMPROVED
POLY_HWR_healthbar, // health bar
POLY_HWR_airbar, // air bar
#endif // FEATURE_HUD_IMPROVED
} POLYTYPE;
typedef enum {
ERR_OK,
ERR_PreferredAdapterNotFound,
ERR_CantCreateWindow,
ERR_CantCreateDirectDraw,
ERR_CantInitRenderer,
ERR_CantCreateDirectInput,
ERR_CantCreateKeyboardDevice,
ERR_CantSetKBCooperativeLevel,
ERR_CantSetKBDataFormat,
ERR_CantAcquireKeyboard,
ERR_CantSetDSCooperativeLevel,
ERR_DD_SetExclusiveMode,
ERR_DD_ClearExclusiveMode,
ERR_SetDisplayMode,
ERR_CreateScreenBuffers,
ERR_GetBackBuffer,
ERR_CreatePalette,
ERR_SetPalette,
ERR_CreatePrimarySurface,
ERR_CreateBackBuffer,
ERR_CreateClipper,
ERR_SetClipperHWnd,
ERR_SetClipper,
ERR_CreateZBuffer,
ERR_AttachZBuffer,
ERR_CreateRenderBuffer,
ERR_CreatePictureBuffer,
ERR_D3D_Create,
ERR_CreateDevice,
ERR_CreateViewport,
ERR_AddViewport,
ERR_SetViewport2,
ERR_SetCurrentViewport,
ERR_ClearRenderBuffer,
ERR_UpdateRenderInfo,
ERR_GetThirdBuffer,
ERR_GoFullScreen,
ERR_GoWindowed,
ERR_WrongBitDepth,
ERR_GetPixelFormat,
ERR_GetDisplayMode,
} ERROR_CODE;
typedef enum {
GBUF_TempAlloc,
GBUF_TexturePages,
GBUF_MeshPointers,
GBUF_Meshes,
GBUF_Anims,
GBUF_Structs,
GBUF_Ranges,
GBUF_Commands,
GBUF_Bones,
GBUF_Frames,
GBUF_RoomTextures,
GBUF_RoomInfos,
GBUF_RoomMesh,
GBUF_RoomDoor,
GBUF_RoomFloor,
GBUF_RoomLights,
GBUF_RoomStaticMeshInfos,
GBUF_FloorData,
GBUF_Items,
GBUF_Cameras,
GBUF_SoundFX,
GBUF_Boxes,
GBUF_Overlaps,
GBUF_GroundZone,
GBUF_FlyZone,
GBUF_AnimatingTextureRanges,
GBUF_CinematicFrames,
GBUF_LoadDemoBuffer,
GBUF_SaveDemoBuffer,
GBUF_CinematicEffects,
GBUF_MummyHeadTurn,
GBUF_ExtraDoorstuff,
GBUF_EffectsArray,
GBUF_CreatureData,
GBUF_CreatureLOT,
GBUF_SampleInfos,
GBUF_Samples,
GBUF_SampleOffsets,
GBUF_RollingBallStuff,
GBUF_SkidooStuff,
GBUF_LoadPiccyBuffer,
GBUF_FMVBuffers,
GBUF_PolygonBuffers,
GBUF_OrderTables,
GBUF_CLUTs,
GBUF_TextureInfos,
GBUF_SpriteInfos,
} GAMEALLOC_BUFFER;
typedef enum {
GSI_Heading_Inventory,
GSI_Heading_Option,
GSI_Heading_Items,
GSI_Heading_GameOver,
GSI_Passport_LoadGame,
GSI_Passport_SaveGame,
GSI_Passport_NewGame,
GSI_Passport_RestartLevel,
GSI_Passport_ExitToTitle,
GSI_Passport_ExitDemo,
GSI_Passport_ExitGame,
GSI_Passport_SelectLevel,
GSI_Passport_SavePosition,
GSI_Detail_SelectDetail,
GSI_Detail_High,
GSI_Detail_Medium,
GSI_Detail_Low,
GSI_Keymap_Walk,
GSI_Keymap_Roll,
GSI_Keymap_Run,
GSI_Keymap_Left,
GSI_Keymap_Right,
GSI_Keymap_Back,
GSI_Keymap_StepLeft,
GSI_Keymap_Reserved1,
GSI_Keymap_StepRight,
GSI_Keymap_Reserved2,
GSI_Keymap_Look,
GSI_Keymap_Jump,
GSI_Keymap_Action,
GSI_Keymap_DrawWeapon,
GSI_Keymap_Reserved3,
GSI_Keymap_Inventory,
GSI_Keymap_Flare,
GSI_Keymap_Step,
GSI_InvItem_Statistics,
GSI_InvItem_Pistols,
GSI_InvItem_Shotgun,
GSI_InvItem_Magnums,
GSI_InvItem_Uzis,
GSI_InvItem_Harpoon,
GSI_InvItem_M16,
GSI_InvItem_Grenade,
GSI_InvItem_Flare,
GSI_InvItem_PistolAmmo,
GSI_InvItem_ShotgunAmmo,
GSI_InvItem_MagnumAmmo,
GSI_InvItem_UziAmmo,
GSI_InvItem_HarpoonAmmo,
GSI_InvItem_M16Ammo,
GSI_InvItem_GrenadeAmmo,
GSI_InvItem_SmallMedipack,
GSI_InvItem_LargeMedipack,
GSI_InvItem_Pickup,
GSI_InvItem_Puzzle,
GSI_InvItem_Key,
GSI_InvItem_Game,
GSI_InvItem_LaraHome,
GSI_String_Loading,
GSI_String_TimeTaken,
GSI_String_SecretsFound,
GSI_String_Location,
GSI_String_Kills,
GSI_String_AmmoUsed,
GSI_String_Hits,
GSI_String_SavesPerformed,
GSI_String_DistanceTravelled,
GSI_String_HealthPacksUsed,
GSI_String_ReleaseVersion,
GSI_String_None,
GSI_String_Finish,
GSI_String_BestTimes,
GSI_String_NoTimesSet,
GSI_String_NA,
GSI_String_CurrentPosition,
GSI_String_FinalStatistics,
GSI_String_Of,
GSI_String_StorySoFar,
} GAME_STRING_ID;
typedef enum {
SSI_DetailLevels,
SSI_DemoMode,
SSI_Sound,
SSI_Controls,
SSI_Gamma,
SSI_SetVolumes,
SSI_UserKeys,
SSI_SaveFileWarning,
SSI_TryAgainQuestion,
SSI_Yes,
SSI_No,
SSI_SaveComplete,
SSI_NoSaveGames,
SSI_NoneValid,
SSI_SaveGameQuestion,
SSI_EmptySlot,
SSI_Off,
SSI_On,
SSI_SetupSoundCard,
SSI_DefaultKeys,
SSI_Dozy,
} SPECIFIC_STRING_ID;
typedef enum {
ICLR_Black,
ICLR_Gray,
ICLR_White,
ICLR_Red,
ICLR_Orange,
ICLR_Yellow,
ICLR_Reserved1,
ICLR_Reserved2,
ICLR_Reserved3,
ICLR_Reserved4,
ICLR_Reserved5,
ICLR_Reserved6,
ICLR_DarkGreen,
ICLR_Green,
ICLR_Cyan,
ICLR_Blue,
ICLR_Magenta,
} INV_COLOURS;
typedef enum {
LWS_AboveWater,
LWS_Underwater,
LWS_Surface,
LWS_Cheat,
LWS_Wade,
} LARA_WATER_STATES;
typedef enum {
RING_Main,
RING_Option,
RING_Keys,
} RING_TYPE;
typedef enum {
GFL_NOLEVEL = -1,
GFL_TITLE,
GFL_NORMAL,
GFL_SAVED,
GFL_DEMO,
GFL_CUTSCENE,
GFL_STORY,
GFL_QUIET,
GFL_MIDSTORY,
} GF_LEVEL_TYPE;
typedef enum {
GFE_PICTURE,
GFE_LIST_START,
GFE_LIST_END,
GFE_PLAYFMV,
GFE_STARTLEVEL,
GFE_CUTSCENE,
GFE_LEVCOMPLETE,
GFE_DEMOPLAY,
GFE_JUMPTO_SEQ,
GFE_END_SEQ,
GFE_SETTRACK,
GFE_SUNSET,
GFE_LOADINGPIC,
GFE_DEADLY_WATER,
GFE_REMOVE_WEAPONS,
GFE_GAMECOMPLETE,
GFE_CUTANGLE,
GFE_NOFLOOR,
GFE_ADD2INV,
GFE_STARTANIM,
GFE_NUMSECRETS,
GFE_KILL2COMPLETE,
GFE_REMOVE_AMMO,
} GF_EVENTS;
typedef enum {
MOOD_BORED,
MOOD_ATTACK,
MOOD_ESCAPE,
MOOD_STALK,
} MOOD_TYPE;
typedef enum {
ITEM_INACTIVE,
ITEM_ACTIVE,
ITEM_DISABLED,
ITEM_INVISIBLE,
} ITEM_STATUS;
typedef enum {
ADDINV_PISTOL,
ADDINV_SHOTGUN,
ADDINV_MAGNUM,
ADDINV_UZI,
ADDINV_HARPOON,
ADDINV_M16,
ADDINV_GRENADE,
ADDINV_PISTOL_AMMO,
ADDINV_SHOTGUN_AMMO,
ADDINV_MAGNUM_AMMO,
ADDINV_UZI_AMMO,
ADDINV_HARPOON_AMMO,
ADDINV_M16_AMMO,
ADDINV_GRENADE_AMMO,
ADDINV_FLARE,
ADDINV_SMALL_MEDIPACK,
ADDINV_LARGE_MEDIPACK,
ADDINV_PICKUP1,
ADDINV_PICKUP2,
ADDINV_PUZZLE1,
ADDINV_PUZZLE2,
ADDINV_PUZZLE3,
ADDINV_PUZZLE4,
ADDINV_KEY1,
ADDINV_KEY2,
ADDINV_KEY3,
ADDINV_KEY4,
} ADDINV_TYPES;
typedef enum {
#ifdef FEATURE_HUD_IMPROVED
CHAR_SECRET1 = 0x13,
#else
CHAR_SECRET1 = 0x7F,
#endif
CHAR_SECRET2,
CHAR_SECRET3,
} CHAR_SECRETS;
/*
* Structs
*/
#pragma pack(push, 1)
// NOTE: there were int items in the original code,
// but it's more important to have wider range
// since negative numbers are not used anyway
typedef struct SortItem_t {
#ifdef FEATURE_VIEW_IMPROVED
UINT64 _0;
UINT64 _1;
#else // FEATURE_VIEW_IMPROVED
DWORD _0;
DWORD _1;
#endif // FEATURE_VIEW_IMPROVED
} SORT_ITEM;
typedef struct RGB888_t {
BYTE red;
BYTE green;
BYTE blue;
} RGB888;
typedef struct GouraudFill_t {
D3DCOLOR clr[4][4];
} GOURAUD_FILL;
typedef struct GouraudOutline_t {
D3DCOLOR clr[9];
} GOURAUD_OUTLINE;
typedef struct DepthQEntry_t {
BYTE index[256];
} DEPTHQ_ENTRY;
typedef struct GouraudEntry_t {
BYTE index[32];
} GOURAUD_ENTRY;
typedef struct DisplayMode_t {
int width;
int height;
int bpp;
VGA_MODE vga;
} DISPLAY_MODE;
typedef struct DisplayModeNode_t {
struct DisplayModeNode_t *next;
struct DisplayModeNode_t *previous;
struct DisplayMode_t body;
} DISPLAY_MODE_NODE;
typedef struct DisplayModeList_t {
struct DisplayModeNode_t *head;
struct DisplayModeNode_t *tail;
DWORD dwCount;
} DISPLAY_MODE_LIST;
typedef struct StringFlagged_t {
LPTSTR lpString;
bool isPresented;
} STRING_FLAGGED;
typedef struct DisplayAdapter_t {
LPGUID lpAdapterGuid;
GUID adapterGuid;
STRING_FLAGGED driverDescription;
STRING_FLAGGED driverName;
#if (DIRECT3D_VERSION >= 0x900)
UINT index;
D3DCAPS9 caps;
DISPLAY_MODE_LIST hwDispModeList;
DISPLAY_MODE_LIST swDispModeList;
DWORD screenWidth;
#else // (DIRECT3D_VERSION >= 0x900)
DDCAPS driverCaps;
DDCAPS helCaps;
GUID deviceGuid;
D3DDEVICEDESC D3DHWDeviceDesc;
DISPLAY_MODE_LIST hwDispModeList;
DISPLAY_MODE_LIST swDispModeList;
DISPLAY_MODE vgaMode1;
DISPLAY_MODE vgaMode2;
DWORD screenWidth;
bool hwRenderSupported;
bool swWindowedSupported;
bool hwWindowedSupported;
bool isVgaMode1Presented;
bool isVgaMode2Presented;
bool perspectiveCorrectSupported;
bool ditherSupported;
bool zBufferSupported;
bool linearFilterSupported;
bool shadeRestricted;
#endif // (DIRECT3D_VERSION >= 0x900)
} DISPLAY_ADAPTER;
typedef struct DisplayAdapterNode_t {
struct DisplayAdapterNode_t *next;
struct DisplayAdapterNode_t *previous;
struct DisplayAdapter_t body;
} DISPLAY_ADAPTER_NODE;
typedef struct DisplayAdapterList_t {
struct DisplayAdapterNode_t *head;
struct DisplayAdapterNode_t *tail;
DWORD dwCount;
} DISPLAY_ADAPTER_LIST;
typedef struct SoundAdapter_t {
GUID *lpAdapterGuid;
GUID adapterGuid;
STRING_FLAGGED description;
STRING_FLAGGED module;
} SOUND_ADAPTER;
typedef struct SoundAdapterNode_t {
struct SoundAdapterNode_t *next;
struct SoundAdapterNode_t *previous;
SOUND_ADAPTER body;
} SOUND_ADAPTER_NODE;
typedef struct SoundAdapterList_t {
struct SoundAdapterNode_t *head;
struct SoundAdapterNode_t *tail;
DWORD dwCount;
} SOUND_ADAPTER_LIST;
typedef struct Joystick_t {
GUID *lpJoystickGuid;
GUID joystickGuid;
STRING_FLAGGED productName;
STRING_FLAGGED instanceName;
#ifdef FEATURE_INPUT_IMPROVED
JOY_INTERFACE iface;
#endif // FEATURE_INPUT_IMPROVED
} JOYSTICK;
typedef struct JoystickNode_t {
struct JoystickNode_t *next;
struct JoystickNode_t *previous;
JOYSTICK body;
} JOYSTICK_NODE;
typedef struct JoystickList_t {
struct JoystickNode_t *head;
struct JoystickNode_t *tail;
DWORD dwCount;
} JOYSTICK_LIST;
typedef struct AppSettings_t {
DISPLAY_ADAPTER_NODE *PreferredDisplayAdapter;
SOUND_ADAPTER_NODE *PreferredSoundAdapter;
JOYSTICK_NODE *PreferredJoystick;
DISPLAY_MODE_NODE *VideoMode;
RENDER_MODE RenderMode;
int WindowWidth;
int WindowHeight;
ASPECT_MODE AspectMode;
bool PerspectiveCorrect;
bool Dither;
bool ZBuffer;
bool BilinearFiltering;
bool TripleBuffering;
bool FullScreen;
bool SoundEnabled;
bool LaraMic;
bool JoystickEnabled;
bool Disable16BitTextures;
bool DontSortPrimitives;
bool FlipBroken;
bool DisableFMV;
TEX_ADJUST_MODE TexelAdjustMode;
int NearestAdjustment;
int LinearAdjustment;
#ifdef FEATURE_VIDEOFX_IMPROVED
int LightingMode;
#endif // FEATURE_VIDEOFX_IMPROVED
} APP_SETTINGS;
struct TEXPAGE_DESC {
#if (DIRECT3D_VERSION >= 0x900)
LPDIRECT3DTEXTURE9 texture;
#else // (DIRECT3D_VERSION >= 0x900)
LPDDS sysMemSurface;
LPDDS vidMemSurface;
LPDIRECTDRAWPALETTE palette;
LPDIRECT3DTEXTURE2 texture3d;
HWR_TEXHANDLE texHandle;
#endif // (DIRECT3D_VERSION >= 0x900)
int width;
int height;
int status;
};
typedef struct PhdUV_t {
UINT16 u;
UINT16 v;
} PHD_UV;
typedef struct PhdTexture_t {
UINT16 drawtype;
UINT16 tpage;
PHD_UV uv[4];
} PHD_TEXTURE;
typedef struct ColorBitMasks_t {
DWORD dwRBitMask;
DWORD dwGBitMask;
DWORD dwBBitMask;
DWORD dwRGBAlphaBitMask;
DWORD dwRBitDepth;
DWORD dwGBitDepth;
DWORD dwBBitDepth;
DWORD dwRGBAlphaBitDepth;
DWORD dwRBitOffset;
DWORD dwGBitOffset;
DWORD dwBBitOffset;
DWORD dwRGBAlphaBitOffset;
} COLOR_BIT_MASKS;
typedef struct TextureFormat_t {
#if (DIRECT3D_VERSION < 0x900)
DDPIXELFORMAT pixelFmt;
COLOR_BIT_MASKS colorBitMasks;
#endif // (DIRECT3D_VERSION < 0x900)
DWORD bpp;
} TEXTURE_FORMAT;
typedef struct TextStrInfo_t {
DWORD flags;
UINT16 textFlags;
UINT16 bgndFlags;
UINT16 outlFlags;
__int16 xPos;
__int16 yPos;
__int16 zPos;
__int16 letterSpacing;
__int16 wordSpacing;
__int16 flashRate;
__int16 flashCount;
__int16 bgndColor;
GOURAUD_FILL *bgndGour;
__int16 outlColour;
GOURAUD_OUTLINE *outlGour;
__int16 bgndSizeX;
__int16 bgndSizeY;
__int16 bgndOffX;
__int16 bgndOffY;
__int16 bgndOffZ;
int scaleH;
int scaleV;
char *pString;
} TEXT_STR_INFO;
typedef struct StringFixed4_t {
char str[4];
} STRING_FIXED4;
typedef struct StringFixed50_t {
char str[50];
} STRING_FIXED50;
typedef struct StringFixed64_t {
char str[64];
} STRING_FIXED64;
typedef struct PcxHeader_t {
BYTE manufacturer;
BYTE version;
BYTE rle;
BYTE bpp;
UINT16 xMin;
UINT16 yMin;
UINT16 xMax;
UINT16 yMax;
UINT16 h_dpi;
UINT16 v_dpi;
RGB888 headerPalette[16];
BYTE reserved;
BYTE planes;
UINT16 bytesPerLine;
UINT16 palInterpret;
UINT16 h_res;
UINT16 v_res;
BYTE reservedData[54];
} PCX_HEADER;
typedef struct TgaHeader_t {
UINT8 idLength;
UINT8 colorMapType;
UINT8 dataTypeCode;
UINT16 colorMapOrigin;
UINT16 colorMapLength;
UINT8 colorMapDepth;
UINT16 xOrigin;
UINT16 yOrigin;
UINT16 width;
UINT16 height;
UINT8 bpp;
UINT8 imageDescriptor;
} TGA_HEADER;
typedef struct BitmapResource_t {
LPBITMAPINFO bmpInfo;
void *bmpData;
HPALETTE hPalette;
DWORD flags;
} BITMAP_RESOURCE;
typedef struct RoomLightTable_t {
int table[32];
} ROOM_LIGHT_TABLE;
typedef struct WavePcmHeader_t {
DWORD dwRiffChunkID;
DWORD dwRiffChunkSize;
DWORD dwFormat;
DWORD dwFmtSubchunkID;
DWORD dwFmtSubchunkSize;
UINT16 wFormatTag;
__int16 nChannels;
int nSamplesPerSec;
int nAvgBytesPerSec;
__int16 nBlockAlign;
UINT16 wBitsPerSample;
DWORD dwDataSubchunkID;
DWORD dwDataSubchunkSize;
} WAVEPCM_HEADER;
typedef struct SampleInfo_t {
__int16 sfxID;
__int16 volume;
__int16 randomness;
UINT16 flags;
} SAMPLE_INFO;
typedef struct SfxInfo_t {
int volume;
int pan;
int sampleIdx;
int freqFactor;
} SFX_INFO;
typedef struct GameFlow_t {
int firstOption;
int titleReplace;
int onDeath_DemoMode;
int onDeath_InGame;
int noInput_Time;
int onDemo_Interrupt;
int onDemo_End;
UINT16 reserved1[18];
UINT16 num_Levels;
UINT16 num_Pictures;
UINT16 num_Titles;
UINT16 num_Fmvs;
UINT16 num_Cutscenes;
UINT16 num_Demos;
UINT16 titleTrack;
__int16 singleLevel;
UINT16 reserved2[16];
UINT16 flags;
UINT16 reserved3[3];
BYTE cypherCode;
BYTE language;
BYTE secretTrack;
BYTE levelCompleteTrack;
UINT16 reserved4[2];
} GAME_FLOW;
typedef struct RequestInfo_t {
UINT16 reqFlags;
UINT16 itemsCount;
UINT16 selected;
UINT16 visibleCount;
UINT16 lineOffset;
UINT16 lineOldOffset;
UINT16 pixWidth;
UINT16 lineHeight;
__int16 xPos;
__int16 yPos;
__int16 zPos;
UINT16 itemStringLen;
char *lpItemStrings1;
char *lpItemStrings2;
DWORD *lpItemFlags1;
DWORD *lpItemFlags2;
DWORD headingFlags1;
DWORD headingFlags2;
DWORD backgroundFlags;
DWORD moreupFlags;
DWORD moredownFlags;
DWORD itemFlags1[24];
DWORD itemFlags2[24];
TEXT_STR_INFO *headingText1;
TEXT_STR_INFO *headingText2;
TEXT_STR_INFO *backgroundText;
TEXT_STR_INFO *moreupText;
TEXT_STR_INFO *moredownText;
TEXT_STR_INFO *itemTexts1[24];
TEXT_STR_INFO *itemTexts2[24];
char headingString1[32];
char headingString2[32];
DWORD renderWidth;
DWORD renderHeight;
} REQUEST_INFO;
typedef struct InventoryItem_t {
char *lpString;
__int16 objectID;
__int16 framesTotal;
__int16 currentFrame;
__int16 goalFrame;
__int16 openFrame;
__int16 animDirection;
__int16 animSpeed;
__int16 animCount;
__int16 xRotPtSel;
__int16 xRotPt;
__int16 xRotSel;
__int16 xRot;
__int16 yRotSel;
__int16 yRot;
__int16 zRot;
int yTransSel;
int yTrans;
int zTransSel;
int zTrans;
int meshesSel;
int meshesDrawn;
__int16 invPos;
void *sprites;
DWORD reserved1;
DWORD reserved2;
DWORD reserved3;
DWORD reserved4;
} INVENTORY_ITEM;
typedef struct StatisticsInfo_t {
DWORD timer;
DWORD shots;
DWORD hits;
DWORD distance;
UINT16 kills;
BYTE secrets;
BYTE mediPacks;
} STATISTICS_INFO;
typedef struct StartInfo_t {
UINT16 pistolAmmo;
UINT16 magnumAmmo;
UINT16 uziAmmo;
UINT16 shotgunAmmo;
UINT16 m16Ammo;
UINT16 grenadeAmmo;
UINT16 harpoonAmmo;
BYTE smallMedipacks;
BYTE largeMedipacks;
BYTE unused;
BYTE flares;
BYTE gunStatus;
BYTE gunType;
UINT16 available : 1;
UINT16 has_pistols : 1;
UINT16 has_magnums : 1;
UINT16 has_uzis : 1;
UINT16 has_shotgun : 1;
UINT16 has_m16 : 1;
UINT16 has_grenade : 1;
UINT16 has_harpoon : 1;
UINT16 pad : 8;
UINT16 reserved;
STATISTICS_INFO statistics;
} START_INFO;
typedef struct SaveGame_t {
START_INFO start[24];
STATISTICS_INFO statistics;
__int16 currentLevel;
bool bonusFlag;
BYTE numPickup[2];
BYTE numPuzzle[4];
BYTE numKey[4];
UINT16 reserved;
BYTE buffer[6272];
} SAVEGAME_INFO;
typedef struct Pos2D_t {
__int16 x;
__int16 y;
} POS_2D;
typedef struct Pos3D_t {
__int16 x;
__int16 y;
__int16 z;
} POS_3D;
typedef struct PhdVector_t {
int x;
int y;
int z;
} PHD_VECTOR;
typedef struct GameVector_t {
int x;
int y;
int z;
__int16 roomNumber;
__int16 boxNumber;
} GAME_VECTOR;
typedef struct ObjectVector_t {
int x;
int y;
int z;
UINT16 data;
UINT16 flags;
} OBJECT_VECTOR;
typedef struct VectorAngles_t {
__int16 yaw;
__int16 pitch;
} VECTOR_ANGLES;
typedef struct Phd3dPos_t {
int x;
int y;
int z;
__int16 rotX;
__int16 rotY;
__int16 rotZ;
} PHD_3DPOS;
typedef struct ItemInfo_t {
int floor;
DWORD touchBits;
DWORD meshBits;
__int16 objectID;
__int16 currentAnimState;
__int16 goalAnimState;
__int16 requiredAnimState;
__int16 animNumber;
__int16 frameNumber;
__int16 roomNumber;
__int16 nextItem;
__int16 nextActive;
__int16 speed;
__int16 fallSpeed;
__int16 hitPoints;
__int16 boxNumber;
__int16 timer;
UINT16 flags; // see IFL_* defines
__int16 shade1;
__int16 shade2;
__int16 carriedItem;
LPVOID data;
PHD_3DPOS pos;
UINT16 active : 1;
UINT16 status : 2; // see ITEM_STATUS enum
UINT16 gravity : 1;
UINT16 hit_status : 1;
UINT16 collidable : 1;
UINT16 looked_at : 1;
UINT16 dynamic_light : 1;
UINT16 clear_body : 1;
UINT16 pad : 7;
} ITEM_INFO;
typedef struct CameraInfo_t {
GAME_VECTOR pos;
GAME_VECTOR target;
CAMERA_TYPE type;
int shift;
DWORD flags;
int fixedCamera;
int numberFrames;
int bounce;
int underwater;
int targetDistance;
int targetSquare;
__int16 targetAngle;
__int16 actualAngle;
__int16 targetElevation;
__int16 box;
__int16 number;
__int16 last;
__int16 timer;
__int16 speed;
ITEM_INFO *item;
ITEM_INFO *last_item;
OBJECT_VECTOR *fixed;
BOOL isLaraMic;
PHD_VECTOR micPos;
} CAMERA_INFO;
typedef struct CollSide_t {
int floor;
int ceiling;
int type;
} COLL_SIDE;
typedef struct CollInfo_t {
COLL_SIDE sideMid;
COLL_SIDE sideFront;
COLL_SIDE sideLeft;
COLL_SIDE sideRight;
int radius;
int badPos;
int badNeg;
int badCeiling;
PHD_VECTOR shift;
PHD_VECTOR old;
__int16 oldAnimState;
__int16 oldAnimNumber;
__int16 oldFrameNumber;
__int16 facing;
__int16 quadrant;
__int16 collType;
__int16 *trigger;
char xTilt;
char zTilt;
char hitByBaddie;
char hitStatic;
UINT16 flags;
} COLL_INFO;
typedef struct ObjectInfo_t {
__int16 nMeshes;
__int16 meshIndex;
int boneIndex;
__int16 *frameBase;
void (__cdecl *initialise)(__int16 itemNumber);
void (__cdecl *control)(__int16 itemNumber);
void (__cdecl *floor)(ITEM_INFO *item, int x, int y, int z, __int16 *height);
void (__cdecl *ceiling)(ITEM_INFO *item, int x, int y, int z, __int16 *height);
void (__cdecl *drawRoutine)(ITEM_INFO *item);
void (__cdecl *collision)(__int16 itemNum, ITEM_INFO *laraItem, COLL_INFO *coll);
__int16 animIndex;
__int16 hitPoints;
__int16 pivotLength;
__int16 radius;
__int16 shadowSize;
UINT16 loaded : 1;
UINT16 intelligent : 1;
UINT16 save_position : 1;
UINT16 save_hitpoints : 1;
UINT16 save_flags : 1;
UINT16 save_anim : 1;
UINT16 semi_transparent : 1;
UINT16 water_creature : 1;
UINT16 pad : 8;
} OBJECT_INFO;
typedef struct PhdMatrix_t {
int _00, _01, _02, _03;
int _10, _11, _12, _13;
int _20, _21, _22, _23;
} PHD_MATRIX;
typedef struct DoorInfo_t {
__int16 room;
__int16 x;
__int16 y;
__int16 z;
POS_3D vertex[4];
} DOOR_INFO;
typedef struct DoorInfos_t {
__int16 wCount;
DOOR_INFO door[];
} DOOR_INFOS;
typedef struct FloorInfo_t {
__int16 index;
__int16 box;
char pitRoom;
char floor;
char skyRoom;
char ceiling;
} FLOOR_INFO;
typedef struct LightInfo_t {
int x;
int y;
int z;
__int16 intensity1;
__int16 intensity2;
int fallOff1;
int fallOff2;
} LIGHT_INFO;
typedef struct MeshInfo_t {
int x;
int y;
int z;
__int16 yRot;
__int16 shade1;
__int16 shade2;
__int16 staticNumber;
} MESH_INFO;
typedef struct RoomInfo_t {
__int16 *data;
DOOR_INFOS *doors;
FLOOR_INFO *floor;
LIGHT_INFO *light;
MESH_INFO *mesh;
int x;
int y;
int z;
int minFloor;
int maxCeiling;
__int16 xSize;
__int16 ySize;
__int16 ambient1;
__int16 ambient2;
__int16 lightMode;
__int16 numLights;
__int16 numMeshes;
__int16 boundLeft;
__int16 boundRight;
__int16 boundTop;
__int16 boundBottom;
UINT16 boundActive;
__int16 left;
__int16 right;
__int16 top;
__int16 bottom;
__int16 itemNumber;
__int16 fxNumber;
__int16 flippedRoom;
UINT16 flags;
} ROOM_INFO;
typedef struct PhdVBuf_t {
float xv;
float yv;
float zv;
float rhw;
float xs;
float ys;
__int16 clip;
__int16 g;
__int16 u;
__int16 v;
} PHD_VBUF;
typedef struct PointInfo_t {
float xv;
float yv;
float zv;
float rhw;
float xs;
float ys;
float u;
float v;
float g;
} POINT_INFO;
typedef struct VertexInfo_t {
float x;
float y;
float rhw;
float u;
float v;
float g;
} VERTEX_INFO;
typedef struct RoomVertexInfo_t {
__int16 x;
__int16 y;
__int16 z;
__int16 lightBase;
BYTE lightTableValue;
BYTE flags;
__int16 lightAdder;
} ROOM_VERTEX_INFO;
typedef struct AnimStruct_t {
__int16 *framePtr;
__int16 interpolation;
__int16 currentAnimState;
int velocity;
int acceleration;
__int16 frameBase;
__int16 frameEnd;
__int16 jumpAnimNum;
__int16 jumpFrameNum;
__int16 numberChanges;
__int16 changeIndex;
__int16 numberCommands;
__int16 commandIndex;
} ANIM_STRUCT;
typedef struct ChangeStruct_t {
__int16 goalAnimState;
__int16 numberRanges;
__int16 rangeIndex;
} CHANGE_STRUCT;
typedef struct RangeStruct_t {
__int16 startFrame;
__int16 endFrame;
__int16 linkAnimNum;
__int16 linkFrameNum;
} RANGE_STRUCT;
typedef struct StaticBounds_t {
__int16 xMin;
__int16 xMax;
__int16 yMin;
__int16 yMax;
__int16 zMin;
__int16 zMax;
} STATIC_BOUNDS;
typedef struct StaticInfo_t {
__int16 meshIndex;
UINT16 flags;
STATIC_BOUNDS drawBounds;
STATIC_BOUNDS collisionBounds;
} STATIC_INFO;
typedef struct PhdSprite_t {
UINT16 texPage;
UINT16 offset;
UINT16 width;
UINT16 height;
__int16 x1;
__int16 y1;
__int16 x2;
__int16 y2;
} PHD_SPRITE;
typedef struct BoxInfo_t {
BYTE left;
BYTE right;
BYTE top;
BYTE bottom;
UINT16 height;
UINT16 overlapIndex;
} BOX_INFO;
typedef struct CineFrameInfo_t {
__int16 xTarget;
__int16 yTarget;
__int16 zTarget;
__int16 zPos;
__int16 yPos;
__int16 xPos;
__int16 fov;
__int16 roll;
} CINE_FRAME_INFO;
typedef struct ControlLayout_t {
#ifdef FEATURE_HUD_IMPROVED
BYTE key[16];
#else // FEATURE_HUD_IMPROVED
UINT16 key[14];
#endif // FEATURE_HUD_IMPROVED
} CONTROL_LAYOUT;
typedef struct InventorySprite_t {
__int16 shape;
__int16 x;
__int16 y;
__int16 z;
int param1;
int param2;
LPVOID gour;
UINT16 invColour;
} INVENTORY_SPRITE;
typedef struct AssaultStats_t {
DWORD bestTime[10];
DWORD bestFinish[10];
DWORD finishCount;
} ASSAULT_STATS;
typedef struct PickupInfo_t {
__int16 timer;
__int16 sprite;
} PICKUP_INFO;
typedef struct InvMotionInfo_t {
__int16 framesCount;
__int16 status;
__int16 statusTarget;
__int16 radiusTarget;
__int16 radiusRate;
__int16 cameraTarget_y;
__int16 cameraRate_y;
__int16 cameraTarget_pitch;
__int16 cameraRate_pitch;
__int16 rotateTarget;
__int16 rotateRate;
__int16 itemTarget_xRotPt;
__int16 itemRate_xRotPt;
__int16 itemTarget_xRot;
__int16 itemRate_xRot;
int itemTarget_yTrans;
int itemRate_yTrans;
int itemTarget_zTrans;
int itemRate_zTrans;
int misc;
} INV_MOTION_INFO;
typedef struct RingInfo_t {
INVENTORY_ITEM **itemList;
__int16 type;
__int16 radius;
__int16 cameraPitch;
__int16 isRotating;
__int16 rotCount;
__int16 currentObj;
__int16 targetObj;
__int16 objCount;
__int16 angleAdder;
__int16 rotAdder;
__int16 rotAdderL;
__int16 rotAdderR;
PHD_3DPOS ringPos;
PHD_3DPOS camera;
PHD_VECTOR light;
INV_MOTION_INFO *motionInfo;
} RING_INFO;
typedef struct BoxNode_t {
__int16 exit_box;
UINT16 search_number;
__int16 next_expansion;
__int16 box_number;
} BOX_NODE;
typedef struct LotInfo_t {
BOX_NODE *node;
__int16 head;
__int16 tail;
UINT16 search_number;
UINT16 block_mask;
__int16 step;
__int16 drop;
__int16 fly;
__int16 zone_count;
__int16 target_box;
__int16 required_box;
PHD_VECTOR target;
} LOT_INFO;
typedef struct FxInfo_t {
PHD_3DPOS pos;
__int16 room_number;
__int16 object_number;
__int16 next_fx;
__int16 next_active;
__int16 speed;
__int16 fallspeed;
__int16 frame_number;
__int16 counter;
__int16 shade;
} FX_INFO;
typedef struct CreatureInfo_t {
__int16 head_rotation;
__int16 neck_rotation;
__int16 maximum_turn;
UINT16 flags;
__int16 item_num;
MOOD_TYPE mood;
LOT_INFO LOT;
PHD_VECTOR target;
ITEM_INFO *enemy;
} CREATURE_INFO;
typedef struct BoatInfo_t {
int turn;
int leftFallspeed;
int rightFallspeed;
__int16 tiltAngle;
__int16 extraRotation;
int water;
int pitch;
} BOAT_INFO;
typedef struct SkidooInfo_t {
__int16 trackMesh;
int turn;
int leftFallspeed;
int rightFallspeed;
__int16 tiltAngle;
__int16 extraRotation;
int pitch;
} SKIDOO_INFO;
typedef struct LaraArm_t {
__int16 *frame_base;
__int16 frame_number;
__int16 anim_number;
__int16 lock;
__int16 y_rot;
__int16 x_rot;
__int16 z_rot;
__int16 flash_gun;
} LARA_ARM;
typedef struct LaraInfo_t {
__int16 item_number;
__int16 gun_status;
__int16 gun_type;
__int16 request_gun_type;
__int16 last_gun_type;
__int16 calc_fallspeed;
__int16 water_status;
__int16 climb_status;
__int16 pose_count;
__int16 hit_frame;
__int16 hit_direction;
__int16 air;
__int16 dive_count;
__int16 death_count;
__int16 current_active;
__int16 spaz_effect_count;
__int16 flare_age;
__int16 skidoo;
__int16 weapon_item;
__int16 back_gun;
__int16 flare_frame;
UINT16 flare_control_left : 1;
UINT16 flare_control_right : 1;
UINT16 extra_anim : 1;
UINT16 look : 1;
UINT16 burn : 1;
UINT16 keep_ducked : 1;
UINT16 CanMonkeySwing : 1;
UINT16 pad : 9;
int water_surface_dist;
PHD_VECTOR last_pos;
FX_INFO *spaz_effect;
DWORD mesh_effects;
__int16 *mesh_ptrs[15];
ITEM_INFO *target;
__int16 target_angles[2];
__int16 turn_rate;
__int16 move_angle;
__int16 head_y_rot;
__int16 head_x_rot;
__int16 head_z_rot;
__int16 torso_y_rot;
__int16 torso_x_rot;
__int16 torso_z_rot;
LARA_ARM left_arm;
LARA_ARM right_arm;
DWORD pistol_ammo;
DWORD magnum_ammo;
DWORD uzi_ammo;
DWORD shotgun_ammo;
DWORD harpoon_ammo;
DWORD grenade_ammo;
DWORD m16_ammo;
CREATURE_INFO *creature;
} LARA_INFO;
typedef struct BiteInfo_t {
int x;
int y;
int z;
int meshIndex;
} BITE_INFO;
typedef struct AIInfo_t {
__int16 zone_number;
__int16 enemy_zone;
int distance;
int ahead;
int bite;
__int16 angle;
__int16 enemy_facing;
} AI_INFO;
typedef struct WeaponInfo_t {
__int16 lockAngles[4];
__int16 leftAngles[4];
__int16 rightAngles[4];
__int16 aimSpeed;
__int16 shotAccuracy;
int gunHeight;
int damage;
int targetDist;
__int16 recoilFrame;
__int16 flashTime;
__int16 sampleNum;
} WEAPON_INFO;
#pragma pack(pop)
#endif // GLOBAL_TYPES_H_INCLUDED
================================================
FILE: global/vars.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef GLOBAL_VARS_H_INCLUDED
#define GLOBAL_VARS_H_INCLUDED
/*
* String Constants
*/
#define GameWindowName "Tomb Raider II"
#define GameClassName "Dude:TombRaiderII:DDWndClass"
#define GameDialogName "Tomb Raider II"
#define DialogClassName "Dude:TombRaiderII:DWWndClass"
#define MessageBoxName "Tomb Raider II"
// Variable macros
#define VAR_U_(address, type) (*(type*)(address)) // uninitialized variable
#define VAR_I_(address, type, value) (*(type*)(address)) // initialized variable (value is just for info)
#define ARRAY_(address, type, length) (*(type(*)length)(address)) // array (can be multidimensional)
/*
* General Variables
*/
// 3D insert function variables
#define ins_trans_quad (*(void(__cdecl **)(int,int,int,int,int)) 0x00470318)
#define ins_poly_trans8 (*(void(__cdecl **)(PHD_VBUF*,__int16)) 0x0047032C)
#define ins_flat_rect (*(void(__cdecl **)(int,int,int,int,int,BYTE)) 0x0047805C)
#ifdef FEATURE_VIDEOFX_IMPROVED
#define ins_sprite (*(void(__cdecl **)(int,int,int,int,int,int,__int16,DWORD)) 0x004B2A10)
#else // FEATURE_VIDEOFX_IMPROVED
#define ins_sprite (*(void(__cdecl **)(int,int,int,int,int,int,__int16)) 0x004B2A10)
#endif // FEATURE_VIDEOFX_IMPROVED
#define ins_objectGT3 (*(__int16*(__cdecl **)(__int16*,int,SORTTYPE)) 0x004B2A1C)
#define ins_objectGT4 (*(__int16*(__cdecl **)(__int16*,int,SORTTYPE)) 0x004B2A20)
#define ins_line (*(void(__cdecl **)(int,int,int,int,int,BYTE)) 0x004B2AE8)
#define ins_objectG4 (*(__int16*(__cdecl **)(__int16*,int,SORTTYPE)) 0x004BCAF8)
#define ins_objectG3 (*(__int16*(__cdecl **)(__int16*,int,SORTTYPE)) 0x004BCB40)
#define SfxFunctions (*(void(__cdecl *(*)[32])(ITEM_INFO*)) 0x004641F8)
// Initialized variables
#define PerspectiveDistance VAR_I_(0x00464060, DWORD, 0x3000000)
#define RhwFactor VAR_I_(0x0046408C, float, 0x14000000.p0)
#define CineTrackID VAR_I_(0x004640B0, int, 1)
#define CineTickRate VAR_I_(0x004640B8, int, 0x8000) // 0x8000 = PHD_ONE/TICKS_PER_FRAME
#define CD_TrackID VAR_I_(0x004640BC, __int16, -1)
#define FlipEffect VAR_I_(0x004640C4, int, -1)
#define AssaultBestTime VAR_I_(0x004641F0, int, -1)
#define GF_NumSecrets VAR_I_(0x004642E8, __int16, 3)
#define CineTargetAngle VAR_I_(0x00464310, __int16, PHD_90)
#define OverlayStatus VAR_I_(0x004644E0, int, 1)
#define InvMainObjectsCount VAR_I_(0x004654E0, __int16, 8)
#ifdef FEATURE_HUD_IMPROVED
extern __int16 InvOptionObjectsCount;
#else // FEATURE_HUD_IMPROVED
#define InvOptionObjectsCount VAR_I_(0x00465604, __int16, 4)
#endif // FEATURE_HUD_IMPROVED
#define GymInvOpenEnabled VAR_I_(0x00465618, BOOL, TRUE)
#define InventoryChosen VAR_I_(0x00465A50, __int16, -1)
#define InventoryMode VAR_I_(0x00465A54, INVENTORY_MODE, INV_TitleMode)
#define SoundVolume VAR_I_(0x00465A5C, __int16, 165) // NOTE: value should be 10
#define MusicVolume VAR_I_(0x00465A60, __int16, 255) // NOTE: value should be 10
#define BGND_PaletteIndex VAR_I_(0x00466400, int, -1)
#define ScreenSizer VAR_I_(0x00466480, double, 1.0)
#define GameSizer VAR_I_(0x00466488, double, 1.0)
#define FadeValue VAR_I_(0x00466490, int, 0x100000)
#define FadeLimit VAR_I_(0x00466494, int, 0x100000)
#define FadeAdder VAR_I_(0x00466498, int, 0x8000)
#define RandomControl VAR_I_(0x00466BB0, int, RANDOM_SEED)
#define RandomDraw VAR_I_(0x00466BB4, int, RANDOM_SEED)
#define PaletteIndex VAR_I_(0x00466BDC, int, -1)
#define DumpX VAR_I_(0x00466BE4, __int16, 25)
#define DumpY VAR_I_(0x00466BE6, __int16, 25)
#define DumpWidth VAR_I_(0x00466BE8, __int16, 50)
#define DumpHeight VAR_I_(0x00466BEA, __int16, 0)
#define DetailLevel VAR_I_(0x00467724, DWORD, 1)
#define MidSort VAR_I_(0x0046C2F0, DWORD, 0)
#define FltViewAspect VAR_I_(0x0046C2F4, float, 0.0)
#define XGen_y0 VAR_I_(0x0046C2F8, int, 0)
#define XGen_y1 VAR_I_(0x0046C2FC, int, 0)
// Uninitialized variables
#define PhdWinTop VAR_U_(0x0046E300, int)
#define LsAdder VAR_U_(0x00470308, int)
#define FltWinBottom VAR_U_(0x0047030C, float)
#define FltResZBuf VAR_U_(0x00470310, float)
#define FltResZ VAR_U_(0x00470314, float)
#define PhdWinHeight VAR_U_(0x0047031C, int)
#define PhdWinCenterX VAR_U_(0x00470320, int)
#define PhdWinCenterY VAR_U_(0x00470324, int)
#define PhdLsYaw VAR_U_(0x00470328, __int16)
#define FltWinTop VAR_U_(0x00470330, float)
#define FltWinLeft VAR_U_(0x00478038, float)
#define PhdWinMinY VAR_U_(0x0047803C, __int16)
#define PhdFarZ VAR_U_(0x00478048, int)
#define FltRhwOPersp VAR_U_(0x0047804C, float)
#define PhdWinBottom VAR_U_(0x00478050, int)
#define PhdPersp VAR_U_(0x00478054, int)
#define PhdWinLeft VAR_U_(0x00478058, int)
#define PhdWinMaxX VAR_U_(0x004B29E0, __int16)
#define PhdNearZ VAR_U_(0x004B29E4, int)
#define FltResZORhw VAR_U_(0x004B29E8, float)
#define FltFarZ VAR_U_(0x004B29EC, float)
#define FltWinCenterX VAR_U_(0x004B29F0, float)
#define FltWinCenterY VAR_U_(0x004B29F4, float)
#define PhdScreenHeight VAR_U_(0x004B29F8, int)
#define PrintSurfacePtr VAR_U_(0x004B29FC, BYTE*)
#define PhdWinMinX VAR_U_(0x004B2A00, __int16)
#define FltPerspONearZ VAR_U_(0x004B2A04, float)
#define FltRhwONearZ VAR_U_(0x004B2A08, float)
#define PhdWinMaxY VAR_U_(0x004B2A0C, __int16)
#define FltNearZ VAR_U_(0x004B2A14, float)
#define PhdMatrixPtr VAR_U_(0x004B2A18, PHD_MATRIX*)
#define FltPersp VAR_U_(0x004B2AA8, float)
#define MatrixW2V VAR_U_(0x004B2AB0, PHD_MATRIX)
#define Info3dPtr VAR_U_(0x004B2AE0, __int16*)
#define PhdWinWidth VAR_U_(0x004B2AE4, int)
#define PhdViewDistance VAR_U_(0x004BCAF0, int)
#define PhdLsPitch VAR_U_(0x004BCAF4, __int16)
#define PhdScreenWidth VAR_U_(0x004BF3C8, int)
#define LsDivider VAR_U_(0x004BF3CC, int)
#define FltWinRight VAR_U_(0x004D6B50, float)
#define LsVectorView VAR_U_(0x004D6B58, PHD_VECTOR)
#define PhdWinRight VAR_U_(0x004D6BE8, int)
#define SurfaceCount VAR_U_(0x004D6BEC, DWORD)
#define Sort3dPtr VAR_U_(0x004D6BF0, SORT_ITEM*)
#define WibbleOffset VAR_U_(0x004D6BFC, int)
#define IsWibbleEffect VAR_U_(0x004D6C00, BOOL)
#define IsWaterEffect VAR_U_(0x004D6C04, BOOL)
#define IsShadeEffect VAR_U_(0x004D6F68, bool)
#define CineCurrentFrame VAR_U_(0x004D7770, int)
#define IsChunkyCamera VAR_U_(0x004D777C, BOOL)
#define NoInputCounter VAR_U_(0x004D7784, int)
#define IsResetFlag VAR_U_(0x004D7788, BOOL)
#define FlipTimer VAR_U_(0x004D778C, int)
#define StopInventory VAR_U_(0x004D7794, BOOL)
#define IsDemoLevelType VAR_U_(0x004D779C, BOOL)
#define IsDemoLoaded VAR_U_(0x004D77A0, BOOL)
#define BoundStart VAR_U_(0x004D77B0, int)
#define BoundEnd VAR_U_(0x004D77B4, int)
#define IsAssaultTimerDisplay VAR_U_(0x004D77D0, BOOL)
#define IsAssaultTimerActive VAR_U_(0x004D77D4, BOOL)
#define IsMonkAngry VAR_U_(0x004D77D8, BOOL)
#define AmmoTextInfo VAR_U_(0x004D791C, TEXT_STR_INFO*)
#define DisplayModeTextInfo VAR_U_(0x004D7920, TEXT_STR_INFO*)
#define DisplayModeInfoTimer VAR_U_(0x004D7924, DWORD)
#define InvMainCurrent VAR_U_(0x004D7928, UINT16)
#define InvKeyObjectsCount VAR_U_(0x004D792C, UINT16)
#define InvKeysCurrent VAR_U_(0x004D7930, UINT16)
#define InvOptionCurrent VAR_U_(0x004D7934, UINT16)
#define InvRingText VAR_U_(0x004D7944, TEXT_STR_INFO*)
#define InvUpArrow1 VAR_U_(0x004D794C, TEXT_STR_INFO*)
#define InvUpArrow2 VAR_U_(0x004D7950, TEXT_STR_INFO*)
#define InvDownArrow1 VAR_U_(0x004D7954, TEXT_STR_INFO*)
#define InvDownArrow2 VAR_U_(0x004D7958, TEXT_STR_INFO*)
#define InputDB VAR_U_(0x004D795C, DWORD)
#define IsInventoryActive VAR_U_(0x004D7968, UINT16)
#define InvDemoMode VAR_U_(0x004D7990, BOOL)
#define IsInvOptionsDelay VAR_U_(0x004D79A4, BOOL)
#define InvOptionsDelayCounter VAR_U_(0x004D79A8, int)
#define SoundOptionLine VAR_U_(0x004D79AC, UINT16)
#define StatsRequester VAR_U_(0x004D79B0, REQUEST_INFO)
#define Assault VAR_U_(0x004D7BC8, ASSAULT_STATS)
#define LevelItemCount VAR_U_(0x004D7C28, int)
#define HealthBarTimer VAR_U_(0x004D7C2C, int)
#define CameraCount VAR_U_(0x004D7C64, DWORD)
#define MinesDetonated VAR_U_(0x004D7E6C, BOOL)
#define BGND_PictureIsReady VAR_U_(0x004D7E78, bool)
#if (DIRECT3D_VERSION >= 0x900)
#define D3DDev VAR_U_(0x004D7EAC, LPDIRECT3DDEVICE9)
#define D3D VAR_U_(0x004D7EB0, LPDIRECT3D9)
#else // (DIRECT3D_VERSION >= 0x900)
#define D3DDev VAR_U_(0x004D7EAC, LPDIRECT3DDEVICE2)
#define D3D VAR_U_(0x004D7EB0, LPDIRECT3D2)
#define D3DView VAR_U_(0x004D7EB4, LPDIRECT3DVIEWPORT2)
#define D3DMaterial VAR_U_(0x004D7EB8, LPDIRECT3DMATERIAL2)
#endif // (DIRECT3D_VERSION >= 0x900)
#define MinWindowClientHeight VAR_U_(0x004D7EC0, int)
#if (DIRECT3D_VERSION < 0x900)
#define DDrawInterface VAR_U_(0x004D7EC4, LPDIRECTDRAW)
#endif // (DIRECT3D_VERSION < 0x900)
#define IsGameWindowChanging VAR_U_(0x004D7EC8, bool)
#define MaxWindowHeight VAR_U_(0x004D7ECC, int)
#if (DIRECT3D_VERSION < 0x900)
#define DDraw VAR_U_(0x004D7ED0, LPDIRECTDRAW2)
#endif // (DIRECT3D_VERSION < 0x900)
#define IsGameWindowCreated VAR_U_(0x004D7ED4, bool)
#define IsGameWindowUpdating VAR_U_(0x004D7ED8, bool)
#define IsDDrawGameWindowShow VAR_U_(0x004D7EDC, bool)
#define MinWindowClientWidth VAR_U_(0x004D7EE0, int)
#define IsGameWindowShow VAR_U_(0x004D7EE4, bool)
#define IsMinWindowSizeSet VAR_U_(0x004D7EE8, bool)
#define MaxWindowClientWidth VAR_U_(0x004D7EEC, int)
#define GameWindowWidth VAR_U_(0x004D7EF0, int)
#define IsMinMaxInfoSpecial VAR_U_(0x004D7EF4, bool)
#define IsGameFullScreen VAR_U_(0x004D7EF8, bool)
#define IsGameWindowMaximized VAR_U_(0x004D7EFC, bool)
#define HGameWindow VAR_U_(0x004D7F00, HWND)
#define GameWindowHeight VAR_U_(0x004D7F04, int)
#define PrimaryDisplayAdapter VAR_U_(0x004D7F08, DISPLAY_ADAPTER_NODE*)
#if (DIRECT3D_VERSION > 0x500)
extern DISPLAY_ADAPTER CurrentDisplayAdapter;
#else // (DIRECT3D_VERSION > 0x500)
#define CurrentDisplayAdapter VAR_U_(0x004D7F10, DISPLAY_ADAPTER)
#endif // (DIRECT3D_VERSION > 0x500)
#define LockedBufferCount VAR_U_(0x004D8328, DWORD)
#define GameWindowPositionX VAR_U_(0x004D832C, int)
#define GameWindowPositionY VAR_U_(0x004D8330, int)
#define DisplayAdapterList VAR_U_(0x004D8338, DISPLAY_ADAPTER_LIST)
#define MaxWindowClientHeight VAR_U_(0x004D8344, int)
#define IsMessageLoopClosed VAR_U_(0x004D8348, bool)
#define MaxWindowWidth VAR_U_(0x004D834C, int)
#define IsMaxWindowSizeSet VAR_U_(0x004D8350, bool)
#define AppResultCode VAR_U_(0x004D8354, DWORD)
#define FullScreenWidth VAR_U_(0x004D8358, int)
#define FullScreenHeight VAR_U_(0x004D835C, int)
#define FullScreenBPP VAR_U_(0x004D8360, int)
#define FullScreenVGA VAR_U_(0x004D8364, int)
#define IsGameToExit VAR_U_(0x004D8368, bool)
#define GameWindow_Y VAR_U_(0x004D836C, int)
#define GameWindow_X VAR_U_(0x004D8370, int)
#define IsGameWindowMinimized VAR_U_(0x004D8374, bool)
#define MinWindowWidth VAR_U_(0x004D8378, int)
#define MinWindowHeight VAR_U_(0x004D837C, int)
#define IsGameWindowActive VAR_U_(0x004D8380, bool)
#define CurrentJoystick VAR_U_(0x004D8388, JOYSTICK)
#define JoystickList VAR_U_(0x004D8540, JOYSTICK_LIST)
#if (DIRECTINPUT_VERSION >= 0x800)
#define DInput VAR_U_(0x004D854C, LPDIRECTINPUT8)
#define IDID_SysKeyboard VAR_U_(0x004D8550, LPDIRECTINPUTDEVICE8)
#define IDID_SysJoystick VAR_U_(0x004D8554, LPDIRECTINPUTDEVICE8)
#else // (DIRECTINPUT_VERSION >= 0x800)
#define DInput VAR_U_(0x004D854C, LPDIRECTINPUT)
#define IDID_SysKeyboard VAR_U_(0x004D8550, LPDIRECTINPUTDEVICE)
#define IDID_SysJoystick VAR_U_(0x004D8554, LPDIRECTINPUTDEVICE)
#endif // (DIRECTINPUT_VERSION >= 0x800)
#define IsVidSizeLock VAR_U_(0x004D855C, BOOL)
#define SoundAdapterList VAR_U_(0x004D8960, SOUND_ADAPTER_LIST)
#define IsSoundEnabled VAR_U_(0x004D8D70, bool)
#if (DIRECTSOUND_VERSION >= 0x800)
#define DSound VAR_U_(0x004D8D74, LPDIRECTSOUND8)
#else // (DIRECTSOUND_VERSION >= 0x800)
#define DSound VAR_U_(0x004D8D74, LPDIRECTSOUND)
#endif // (DIRECTSOUND_VERSION >= 0x800)
#define CurrentSoundAdapter VAR_U_(0x004D8E78, SOUND_ADAPTER)
#define PrimarySoundAdapter VAR_U_(0x004D8E9C, SOUND_ADAPTER_NODE*)
#if (DIRECT3D_VERSION >= 0x900)
extern SWR_BUFFER RenderBuffer;
extern SWR_BUFFER PictureBuffer;
#else // (DIRECT3D_VERSION >= 0x900)
#define RenderBufferSurface VAR_U_(0x004D8EA0, LPDDS)
#define DDrawClipper VAR_U_(0x004D8EA4, LPDIRECTDRAWCLIPPER)
#define ThirdBufferSurface VAR_U_(0x004D92A8, LPDDS)
#define PictureBufferSurface VAR_U_(0x004D92AC, LPDDS)
#define ZBufferSurface VAR_U_(0x004D92B0, LPDDS)
#define DDrawPalette VAR_U_(0x004D92B4, LPDIRECTDRAWPALETTE)
#define PrimaryBufferSurface VAR_U_(0x004D92B8, LPDDS)
#define ColorBitMasks VAR_U_(0x004D92D8, COLOR_BIT_MASKS)
#define GameVidBufRect VAR_U_(0x004D9308, RECT)
#endif // (DIRECT3D_VERSION >= 0x900)
#define GameVidRect VAR_U_(0x004D9318, RECT)
#define GameVidWidth VAR_U_(0x004D9328, int)
#define GameVidHeight VAR_U_(0x004D932C, int)
#define GameVidBPP VAR_U_(0x004D9330, int)
#if (DIRECT3D_VERSION < 0x900)
#define GameVidBufWidth VAR_U_(0x004D9334, int)
#define GameVidBufHeight VAR_U_(0x004D9338, int)
#endif // (DIRECT3D_VERSION < 0x900)
#define UvAdd VAR_U_(0x004D933C, int)
#if (DIRECT3D_VERSION < 0x900)
#define GameVid_IsVga VAR_U_(0x004D9340, bool)
#define GameVid_IsWindowedVga VAR_U_(0x004D9341, bool)
#define GameVid_IsFullscreenVga VAR_U_(0x004D9342, bool)
#define IsWindowedVGA VAR_U_(0x004D9343, bool)
#define Is16bitTextures VAR_U_(0x004D9344, bool)
#define NeedToReloadTextures VAR_U_(0x004D9345, bool)
#define BackBufferSurface VAR_U_(0x004D9348, LPDDS)
#endif // (DIRECT3D_VERSION < 0x900)
#define HwrTexturePagesCount VAR_U_(0x004D9350, DWORD)
#define ReadFileBytesCounter VAR_U_(0x004D9BE0, DWORD)
#define LevelFilePalettesOffset VAR_U_(0x004D9BE4, LONG)
#define LevelFileTexPagesOffset VAR_U_(0x004D9BE8, LONG)
#define Meshes VAR_U_(0x004D9D80, __int16*)
#define FloorData VAR_U_(0x004D9D84, __int16*)
#define TextureInfoCount VAR_U_(0x004D9E88, DWORD)
#define LevelFileDepthQOffset VAR_U_(0x004D9E8C, LONG)
#define DriveLetter VAR_U_(0x004D9E98, char)
#define IsFmvPlaying VAR_U_(0x004D9E9C, BOOL)
#define MovieContext VAR_U_(0x004D9EA0, LPVOID)
#define FmvContext VAR_U_(0x004D9EA4, LPVOID)
#define FmvSoundContext VAR_U_(0x004D9EA8, LPVOID)
#define SavedGamesCount VAR_U_(0x004D9EAC, DWORD)
#define CurrentLevel VAR_U_(0x004D9EB0, int)
#define IsLevelComplete VAR_U_(0x004D9EB4, BOOL)
#define SaveCounter VAR_U_(0x004D9EB8, DWORD)
#define CurrentTexSource VAR_U_(0x00519EC8, HWR_TEXHANDLE)
#define HWR_VertexPtr VAR_U_(0x00519F50, D3DTLVERTEX*)
#define ZEnableState VAR_U_(0x00519F54, bool)
#define AlphaBlendEnabler VAR_U_(0x00519F58, D3DRENDERSTATETYPE)
#define ColorKeyState VAR_U_(0x00519F5C, bool)
#define ZWriteEnableState VAR_U_(0x00519F60, bool)
#define GameMemorySize VAR_U_(0x00519FF0, DWORD)
#define GameAllocMemPointer VAR_U_(0x00519FF4, BYTE*)
#define GameAllocMemUsed VAR_U_(0x00519FF8, DWORD)
#define GameAllocMemFree VAR_U_(0x00519FFC, DWORD)
#define GameMemoryPointer VAR_U_(0x0051A0BC, BYTE*)
#define InputStatus VAR_U_(0x0051A1F8, DWORD)
#define IsVidModeLock VAR_U_(0x0051A1FC, bool)
#define JoyKeys VAR_U_(0x0051A200, DWORD)
#define GameModule VAR_U_(0x0051A228, HINSTANCE)
#define CommandLinePtr VAR_U_(0x0051A22C, LPTSTR)
#define KeyCursor VAR_U_(0x0051A238, int)
#define PassportTextInfo VAR_U_(0x0051A2BC, TEXT_STR_INFO*)
#define PhdWinRect VAR_U_(0x0051B908, RECT)
#define HiRes VAR_U_(0x0051B918, int)
#define AnimatedTextureRanges VAR_U_(0x0051B91C, __int16*)
#define WinVidNeedToResetBuffers VAR_U_(0x0051BC20, bool)
#define IsWet VAR_U_(0x0051BC28, BOOL)
#if defined(FEATURE_NOLEGACY_OPTIONS) || defined(FEATURE_VIDEOFX_IMPROVED)
extern APP_SETTINGS ChangedAppSettings;
#else // defined(FEATURE_NOLEGACY_OPTIONS) || defined(FEATURE_VIDEOFX_IMPROVED)
#define ChangedAppSettings VAR_U_(0x0051BC40, APP_SETTINGS)
#endif // defined(FEATURE_NOLEGACY_OPTIONS) || defined(FEATURE_VIDEOFX_IMPROVED)
#define SE_PropSheetWndHandle VAR_U_(0x0051BC7C, HWND)
#define SE_OldPropSheetWndProc VAR_U_(0x0051BC80, WNDPROC)
#define SE_DisplayAdapter VAR_U_(0x0051BC8C, DISPLAY_ADAPTER_NODE*)
#define SoundDialogHandle VAR_U_(0x0051BC90, HWND)
#define GraphicsDialogHandle VAR_U_(0x0051BC9C, HWND)
#define SE_FullScreenMode VAR_U_(0x0051BCA0, DISPLAY_MODE)
#if defined(FEATURE_NOLEGACY_OPTIONS) || defined(FEATURE_VIDEOFX_IMPROVED)
extern APP_SETTINGS SavedAppSettings;
#else // defined(FEATURE_NOLEGACY_OPTIONS) || defined(FEATURE_VIDEOFX_IMPROVED)
#define SavedAppSettings VAR_U_(0x0051BCB0, APP_SETTINGS)
#endif // defined(FEATURE_NOLEGACY_OPTIONS) || defined(FEATURE_VIDEOFX_IMPROVED)
#define IsSetupDialogCentered VAR_U_(0x0051BCF0, bool)
#define IsTitleLoaded VAR_U_(0x0051BD90, BOOL)
#define S_MasterVolume VAR_U_(0x0051BD98, DWORD)
#define MciDeviceID VAR_U_(0x0051BD9C, MCIDEVICEID)
#define CD_LoopTrack VAR_U_(0x0051BDA0, int)
#define CD_LoopCounter VAR_U_(0x0051BDA4, int)
#define TextureFormat VAR_U_(0x0051C1A8, TEXTURE_FORMAT)
#define TexturesAlphaChannel VAR_U_(0x0051C1FC, bool)
#define TexturesHaveCompatibleMasks VAR_U_(0x0051C1FD, bool)
#define HKey VAR_U_(0x0051C290, HKEY)
#define RegKeyDisposition VAR_U_(0x0051C294, DWORD)
#define TextStringCount VAR_U_(0x0051D6A0, __int16)
#define SampleInfoCount VAR_U_(0x0051E6C0, DWORD)
#define SoundIsActive VAR_U_(0x0051E6C4, BOOL)
#define SampleInfos VAR_U_(0x0051E9C4, SAMPLE_INFO*)
#define SG_Point VAR_U_(0x0051E9C8, BYTE*)
#define SaveGame VAR_U_(0x0051E9E0, SAVEGAME_INFO)
#define SG_Count VAR_U_(0x005206A4, DWORD)
#define Lara VAR_U_(0x005206E0, LARA_INFO)
#define LaraItem VAR_U_(0x005207BC, ITEM_INFO*)
#define Effects VAR_U_(0x005207C0, FX_INFO *)
#define NextItemFree VAR_U_(0x005207C6, __int16)
#define NextItemActive VAR_U_(0x005207C8, __int16)
#define NextEffectActive VAR_U_(0x005207CA, __int16)
#define PrevItemActive VAR_U_(0x005207CC, __int16)
#define SoundFxCount VAR_U_(0x00521FDC, DWORD)
#define SoundFx VAR_U_(0x00521FE0, OBJECT_VECTOR*)
#define AnimFrames VAR_U_(0x005251B0, __int16*)
#define MeshPtr VAR_U_(0x005252B0, __int16**)
#define OutsideCamera VAR_U_(0x005252B4, int)
#define DrawRoomsCount VAR_U_(0x005252B8, int)
#define Anims VAR_U_(0x005258F4, ANIM_STRUCT*)
#define OutsideBottom VAR_U_(0x00525B00, int)
#define AnimRanges VAR_U_(0x00525B04, RANGE_STRUCT*)
#define AnimCommands VAR_U_(0x00525B08, __int16*)
#define AnimBones VAR_U_(0x00525BE8, int*)
#define DynamicLightCount VAR_U_(0x00525BEC, DWORD)
#define OutsideLeft VAR_U_(0x00526178, int)
#define AnimChanges VAR_U_(0x0052617C, CHANGE_STRUCT*)
#define RoomCount VAR_U_(0x00526180, __int16)
#define RoomInfo VAR_U_(0x0052618C, ROOM_INFO*)
#define UnderwaterCamera VAR_U_(0x00526190, int)
#define SunsetTimer VAR_U_(0x00526194, DWORD)
#define OutsideRight VAR_U_(0x00526198, int)
#define OutsideTop VAR_U_(0x005261AC, int)
#define DemoPtr VAR_U_(0x005261B0, LPVOID)
#define DemoCount VAR_U_(0x005261B4, int)
#define FlipStatus VAR_U_(0x00526240, int)
#define TriggerPtr VAR_U_(0x00526288, __int16*)
#define Items VAR_U_(0x005262F0, ITEM_INFO*)
#define IsCinematicLoaded VAR_U_(0x005262F4, __int16)
#define CineFramesCount VAR_U_(0x005262F6, __int16)
#define CineFrames VAR_U_(0x005262F8, CINE_FRAME_INFO*)
#define CineLevelID VAR_U_(0x00526312, __int16)
#define CineFrameIdx VAR_U_(0x00526314, __int16)
#define Camera VAR_U_(0x00526320, CAMERA_INFO)
#define Overlaps VAR_U_(0x005263C8, UINT16*)
#define Boxes VAR_U_(0x005263CC, BOX_INFO*)
#define BoxesCount VAR_U_(0x005263D0, DWORD)
// Initialized arrays
#define TrackIDs ARRAY_(0x004642F0, __int16, [16]) /* = {2, 0}; */
#define InvSpriteMusicVolume ARRAY_(0x00464718, INVENTORY_SPRITE, [9]) /* = {
{2, -66, -80, 32, 132, 0, 0x004645DC, ICLR_Gray},
{2, -66, -65, 32, 132, 0, 0x004645E0, ICLR_Gray},
{2, -66, -80, 32, 0, 16, 0x004645DC, ICLR_Gray},
{2, 66, -80, 32, 0, 16, 0x004645E0, ICLR_Gray},
{3, -65, -79, 32, 131, 14, 0x004645E8, ICLR_Gray},
{3, -64, -78, 32, 129, 12, 0x004645E8, ICLR_Gray},
{4, -63, -77, 32, 126, 10, 0x004645F8, ICLR_Red},
{4, -63, -77, 33, 127, 10, NULL, 0},
{0, 0, 0, 0, 0, 0, NULL, 0},
}; */
#define InvSpriteSoundVolume ARRAY_(0x004647E0, INVENTORY_SPRITE, [9]) /* = {
{2, -66, -56, 32, 132, 0, 0x004645DC, ICLR_Gray},
{2, -66, -41, 32, 132, 0, 0x004645E0, ICLR_Gray},
{2, -66, -56, 32, 0, 16, 0x004645DC, ICLR_Gray},
{2, 66, -56, 32, 0, 16, 0x004645E0, ICLR_Gray},
{3, -65, -55, 32, 131, 14, 0x004645E8, ICLR_Gray},
{3, -64, -54, 32, 129, 12, 0x004645E8, ICLR_Gray},
{4, -63, -53, 32, 126, 10, 0x00464600, ICLR_Blue},
{4, -63, -53, 33, 127, 10, NULL, 0},
{0, 0, 0, 0, 0, 0, NULL, 0},
}; */
#define InvSpriteMusicVolumeLow ARRAY_(0x004648A8, INVENTORY_SPRITE, [9]) /* = {
{2, -66, -80, 32, 132, 0, 0x00464610, ICLR_Gray},
{2, -66, -65, 32, 132, 0, 0x00464614, ICLR_Gray},
{2, -66, -80, 32, 0, 16, 0x00464610, ICLR_Gray},
{2, 66, -80, 32, 0, 16, 0x00464614, ICLR_Gray},
{3, -65, -79, 32, 131, 14, 0x00464618, ICLR_Gray},
{3, -64, -78, 32, 129, 12, 0x00464618, ICLR_Gray},
{4, -63, -77, 32, 126, 10, 0x00464620, ICLR_Red},
{4, -63, -77, 33, 127, 10, NULL, 0},
{0, 0, 0, 0, 0, 0, NULL, 0},
}; */
#define InvSpriteSoundVolumeLow ARRAY_(0x00464970, INVENTORY_SPRITE, [9]) /* = {
{2, -66, -56, 32, 132, 0, 0x00464610, ICLR_Gray},
{2, -66, -41, 32, 132, 0, 0x00464614, ICLR_Gray},
{2, -66, -56, 32, 0, 16, 0x00464610, ICLR_Gray},
{2, 66, -56, 32, 0, 16, 0x00464614, ICLR_Gray},
{3, -65, -55, 32, 131, 14, 0x00464618, ICLR_Gray},
{3, -64, -54, 32, 129, 12, 0x00464618, ICLR_Gray},
{4, -63, -53, 32, 126, 10, 0x00464628, ICLR_Blue},
{4, -63, -53, 33, 127, 10, NULL, 0},
{0, 0, 0, 0, 0, 0, NULL, 0},
}; */
#define Weapons ARRAY_(0x00465AE0, WEAPON_INFO, [10]) /* = {
{ // LGT_Unarmed
{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0,
},
{ // LGT_Pistols
{ -60*PHD_DEGREE, 60*PHD_DEGREE, -60*PHD_DEGREE, 60*PHD_DEGREE},
{-170*PHD_DEGREE, 60*PHD_DEGREE, -80*PHD_DEGREE, 80*PHD_DEGREE},
{ -60*PHD_DEGREE, 170*PHD_DEGREE, -80*PHD_DEGREE, 80*PHD_DEGREE},
10*PHD_DEGREE, 8*PHD_DEGREE, 650, 1, 0x2000, 9, 3, 8,
},
{ // LGT_Magnums
{ -60*PHD_DEGREE, 60*PHD_DEGREE, -60*PHD_DEGREE, 60*PHD_DEGREE},
{-170*PHD_DEGREE, 60*PHD_DEGREE, -80*PHD_DEGREE, 80*PHD_DEGREE},
{ -60*PHD_DEGREE, 170*PHD_DEGREE, -80*PHD_DEGREE, 80*PHD_DEGREE},
10*PHD_DEGREE, 8*PHD_DEGREE, 650, 2, 0x2000, 9, 3, 21,
},
{ // LGT_Uzis
{ -60*PHD_DEGREE, 60*PHD_DEGREE, -60*PHD_DEGREE, 60*PHD_DEGREE},
{-170*PHD_DEGREE, 60*PHD_DEGREE, -80*PHD_DEGREE, 80*PHD_DEGREE},
{ -60*PHD_DEGREE, 170*PHD_DEGREE, -80*PHD_DEGREE, 80*PHD_DEGREE},
10*PHD_DEGREE, 8*PHD_DEGREE, 650, 1, 0x2000, 3, 3, 43,
},
{ // LGT_Shotgun
{-60*PHD_DEGREE, 60*PHD_DEGREE, -55*PHD_DEGREE, 55*PHD_DEGREE},
{-80*PHD_DEGREE, 80*PHD_DEGREE, -65*PHD_DEGREE, 65*PHD_DEGREE},
{-80*PHD_DEGREE, 80*PHD_DEGREE, -65*PHD_DEGREE, 65*PHD_DEGREE},
10*PHD_DEGREE, 0, 500, 3, 0x2000, 9, 3, 45,
},
{ // LGT_M16
{-60*PHD_DEGREE, 60*PHD_DEGREE, -55*PHD_DEGREE, 55*PHD_DEGREE},
{-80*PHD_DEGREE, 80*PHD_DEGREE, -65*PHD_DEGREE, 65*PHD_DEGREE},
{-80*PHD_DEGREE, 80*PHD_DEGREE, -65*PHD_DEGREE, 65*PHD_DEGREE},
10*PHD_DEGREE, 4*PHD_DEGREE, 500, 3, 0x3000, 0, 3, 0,
},
{ // LGT_Grenade
{-60*PHD_DEGREE, 60*PHD_DEGREE, -55*PHD_DEGREE, 55*PHD_DEGREE},
{-80*PHD_DEGREE, 80*PHD_DEGREE, -65*PHD_DEGREE, 65*PHD_DEGREE},
{-80*PHD_DEGREE, 80*PHD_DEGREE, -65*PHD_DEGREE, 65*PHD_DEGREE},
10*PHD_DEGREE, 8*PHD_DEGREE, 500, 30, 0x2000, 0, 2, 0,
},
{ // LGT_Harpoon
{-60*PHD_DEGREE, 60*PHD_DEGREE, -65*PHD_DEGREE, 65*PHD_DEGREE},
{-80*PHD_DEGREE, 80*PHD_DEGREE, -75*PHD_DEGREE, 75*PHD_DEGREE},
{-80*PHD_DEGREE, 80*PHD_DEGREE, -75*PHD_DEGREE, 75*PHD_DEGREE},
10*PHD_DEGREE, 8*PHD_DEGREE, 500, 4, 0x2000, 0, 2, 0,
},
{ // LGT_Flare
{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0,
},
{ // LGT_Skidoo
{-30*PHD_DEGREE, 30*PHD_DEGREE, -55*PHD_DEGREE, 55*PHD_DEGREE},
{-30*PHD_DEGREE, 30*PHD_DEGREE, -55*PHD_DEGREE, 55*PHD_DEGREE},
{-30*PHD_DEGREE, 30*PHD_DEGREE, -55*PHD_DEGREE, 55*PHD_DEGREE},
10*PHD_DEGREE, 8*PHD_DEGREE, 400, 3, 0x2000, 0, 2, 43,
},
}; */
#define SaveSlotFlags ARRAY_(0x00466B80, __int16, [16]) /* = {-1, 0}; */
#ifdef FEATURE_HUD_IMPROVED
extern CONTROL_LAYOUT Layout[3];
#else // FEATURE_HUD_IMPROVED
#define Layout ARRAY_(0x00466F58, CONTROL_LAYOUT, [2]) /* = {
{ // Default Layout
DIK_UP,
DIK_DOWN,
DIK_LEFT,
DIK_RIGHT,
DIK_DELETE,
DIK_NEXT,
DIK_RSHIFT,
DIK_RMENU,
DIK_RCONTROL,
DIK_SPACE,
DIK_SLASH,
DIK_NUMPAD0,
DIK_END,
DIK_ESCAPE,
},
{ // User Layout
DIK_NUMPAD8,
DIK_NUMPAD2,
DIK_NUMPAD4,
DIK_NUMPAD6,
DIK_NUMPAD7,
DIK_NUMPAD9,
DIK_NUMPAD1,
DIK_ADD,
DIK_NUMPADENTER,
DIK_NUMPAD3,
DIK_SUBTRACT,
DIK_NUMPAD0,
DIK_NUMPAD5,
DIK_DECIMAL,
},
}; */
#endif // FEATURE_HUD_IMPROVED
// Uninitialized arrays
#define GouraudTable ARRAY_(0x0046C300, GOURAUD_ENTRY, [256])
#ifdef FEATURE_EXTENDED_LIMITS
extern PHD_SPRITE PhdSpriteInfo[2048];
#else // FEATURE_EXTENDED_LIMITS
#define PhdSpriteInfo ARRAY_(0x0046E308, PHD_SPRITE, [512])
#endif // FEATURE_EXTENDED_LIMITS
#if defined(FEATURE_EXTENDED_LIMITS) || defined(FEATURE_VIEW_IMPROVED)
extern SORT_ITEM SortBuffer[16000];
extern __int16 Info3dBuffer[480000];
#else // defined(FEATURE_EXTENDED_LIMITS) || defined(FEATURE_VIEW_IMPROVED)
#define SortBuffer ARRAY_(0x00470338, SORT_ITEM, [4000])
#define Info3dBuffer ARRAY_(0x00478060, __int16, [120000])
#endif // defined(FEATURE_EXTENDED_LIMITS) || defined(FEATURE_VIEW_IMPROVED)
#define RandomTable ARRAY_(0x004B2A28, int, [32])
#ifdef FEATURE_EXTENDED_LIMITS
extern PHD_TEXTURE PhdTextureInfo[0x2000];
#else // FEATURE_EXTENDED_LIMITS
#define PhdTextureInfo ARRAY_(0x004B2AF0, PHD_TEXTURE, [0x800])
#endif // FEATURE_EXTENDED_LIMITS
#define ShadesTable ARRAY_(0x004BCB00, __int16, [32])
#define MatrixStack ARRAY_(0x004BCB48, PHD_MATRIX, [40])
#define DepthQTable ARRAY_(0x004BD2C8, DEPTHQ_ENTRY, [32])
#define DepthQIndex ARRAY_(0x004BF2C8, BYTE, [256])
#define PhdVBuf ARRAY_(0x004BF3D0, PHD_VBUF, [1500])
#ifdef FEATURE_EXTENDED_LIMITS
extern BYTE *TexturePageBuffer8[128];
#else // FEATURE_EXTENDED_LIMITS
#define TexturePageBuffer8 ARRAY_(0x004D6AD0, BYTE*, [32])
#endif // FEATURE_EXTENDED_LIMITS
#define WibbleTable ARRAY_(0x004D6B68, float, [32])
#define GamePalette16 ARRAY_(0x004D7370, PALETTEENTRY, [256])
#define InvItemText ARRAY_(0x004D7938, TEXT_STR_INFO*, [2])
#define InventoryExtraData ARRAY_(0x004D7970, int, [8])
#define SfxInfos ARRAY_(0x004D7C68, SFX_INFO, [32])
#ifdef FEATURE_BACKGROUND_IMPROVED
extern int BGND_TexturePageIndexes[64];
extern HWR_TEXHANDLE BGND_PageHandles[64];
#else // FEATURE_BACKGROUND_IMPROVED
#define BGND_TexturePageIndexes ARRAY_(0x004D7E80, int, [5])
#define BGND_PageHandles ARRAY_(0x004D7E98, HWR_TEXHANDLE, [5])
#endif // FEATURE_BACKGROUND_IMPROVED
#ifdef FEATURE_EXTENDED_LIMITS
extern DWORD SampleFreqs[370];
extern LPDIRECTSOUNDBUFFER SampleBuffers[370];
#else // FEATURE_EXTENDED_LIMITS
#define SampleFreqs ARRAY_(0x004D8560, DWORD, [256])
#define SampleBuffers ARRAY_(0x004D8970, LPDIRECTSOUNDBUFFER, [256])
#endif // FEATURE_EXTENDED_LIMITS
#define ChannelSamples ARRAY_(0x004D8D78, DWORD, [32])
#define ChannelBuffers ARRAY_(0x004D8DF8, LPDIRECTSOUNDBUFFER, [32])
#define WinVidPalette ARRAY_(0x004D8EA8, PALETTEENTRY, [256])
#ifdef FEATURE_EXTENDED_LIMITS
extern BYTE LabTextureUVFlags[0x2000];
#else // FEATURE_EXTENDED_LIMITS
#define LabTextureUVFlags ARRAY_(0x004D93E0, BYTE, [0x800])
#endif // FEATURE_EXTENDED_LIMITS
#define LevelFileName ARRAY_(0x004D9D88, char, [256])
#ifdef FEATURE_EXTENDED_LIMITS
extern D3DTLVERTEX HWR_VertexBuffer[0x8000];
extern HWR_TEXHANDLE HWR_PageHandles[128];
extern int HWR_TexturePageIndexes[128];
#else // FEATURE_EXTENDED_LIMITS
#define HWR_VertexBuffer ARRAY_(0x004D9EC8, D3DTLVERTEX, [0x2000])
#define HWR_PageHandles ARRAY_(0x00519ED0, HWR_TEXHANDLE, [32])
#define HWR_TexturePageIndexes ARRAY_(0x00519F68, int, [32])
#endif // FEATURE_EXTENDED_LIMITS
#ifdef FEATURE_HUD_IMPROVED
extern bool ConflictLayout[ARRAY_SIZE(Layout->key)];
#else // FEATURE_HUD_IMPROVED
#define ConflictLayout ARRAY_(0x0051A0C0, BOOL, [14])
#endif // FEATURE_HUD_IMPROVED
#define DIKeys ARRAY_(0x0051A0F8, BYTE, [256])
#define ControlTextInfo ARRAY_(0x0051A2F0, TEXT_STR_INFO*, [2])
#define RoomLightTables ARRAY_(0x0051A2F8, ROOM_LIGHT_TABLE, [32])
#define WaterPalette ARRAY_(0x0051B2F8, RGB888, [256])
#define PicPalette ARRAY_(0x0051B5F8, RGB888, [256])
#define RoomLightShades ARRAY_(0x0051B8F8, int, [4])
#define GamePalette8 ARRAY_(0x0051B920, RGB888, [256])
#define StringToShow ARRAY_(0x0051BD10, char, [128])
#define TextInfoTable ARRAY_(0x0051C820, TEXT_STR_INFO, [64])
#define TheStrings ARRAY_(0x0051D6C0, STRING_FIXED64, [64])
#define SampleLut ARRAY_(0x0051E6E0, __int16, [370])
#define SaveGameStrings1 ARRAY_(0x005207E0, STRING_FIXED50, [24])
#define RequesterItemFlags2 ARRAY_(0x00520CA0, DWORD, [24])
#define RequesterItemFlags1 ARRAY_(0x00520D00, DWORD, [24])
#define InvColours ARRAY_(0x005216E0, UINT16, [17])
#define SaveGameStrings2 ARRAY_(0x00521720, STRING_FIXED50, [24])
#define SaveGameItemFlags2 ARRAY_(0x00521BE0, DWORD, [24])
#define SaveGameItemFlags1 ARRAY_(0x00521C40, DWORD, [24])
#define PickupInfos ARRAY_(0x00521CA0, PICKUP_INFO, [12])
#define Objects ARRAY_(0x00522000, OBJECT_INFO, [265])
#ifdef FEATURE_EXTENDED_LIMITS
extern LIGHT_INFO DynamicLights[64];
extern int BoundRooms[1024];
extern __int16 DrawRoomsArray[1024];
extern STATIC_INFO StaticObjects[256];
#else // FEATURE_EXTENDED_LIMITS
#define DynamicLights ARRAY_(0x005251C0, LIGHT_INFO, [10])
#define BoundRooms ARRAY_(0x00525900, int, [128])
#define DrawRoomsArray ARRAY_(0x00525B20, __int16, [100])
#define StaticObjects ARRAY_(0x00525C00, STATIC_INFO, [50])
#endif // FEATURE_EXTENDED_LIMITS
#define CD_Flags ARRAY_(0x005261C0, __int16, [64])
#define FlipMaps ARRAY_(0x00526260, int, [10])
#define GroundZones ARRAY_(0x005263A0, __int16*, [8])
#define FlyZones ARRAY_(0x005263C0, __int16*, [2])
/*
* GameFlow/Inventory Variables
*/
// GameFlow/Inventory initialized variables
#define LoadGameRequester VAR_I_(0x00465620, REQUEST_INFO, "...")
#define SaveGameRequester VAR_I_(0x00465838, REQUEST_INFO, "...")
#define InvCompassOption VAR_I_(0x00464A90, INVENTORY_ITEM, "...")
#define InvPistolOption VAR_I_(0x00464AE0, INVENTORY_ITEM, "...")
#define InvFlareOption VAR_I_(0x00464B30, INVENTORY_ITEM, "...")
#define InvShotgunOption VAR_I_(0x00464B80, INVENTORY_ITEM, "...")
#define InvMagnumOption VAR_I_(0x00464BD0, INVENTORY_ITEM, "...")
#define InvUziOption VAR_I_(0x00464C20, INVENTORY_ITEM, "...")
#define InvHarpoonOption VAR_I_(0x00464C70, INVENTORY_ITEM, "...")
#define InvM16Option VAR_I_(0x00464CC0, INVENTORY_ITEM, "...")
#define InvGrenadeOption VAR_I_(0x00464D10, INVENTORY_ITEM, "...")
#define InvPistolAmmoOption VAR_I_(0x00464D60, INVENTORY_ITEM, "...")
#define InvShotgunAmmoOption VAR_I_(0x00464DB0, INVENTORY_ITEM, "...")
#define InvMagnumAmmoOption VAR_I_(0x00464E00, INVENTORY_ITEM, "...")
#define InvUziAmmoOption VAR_I_(0x00464E50, INVENTORY_ITEM, "...")
#define InvHarpoonAmmoOption VAR_I_(0x00464EA0, INVENTORY_ITEM, "...")
#define InvM16AmmoOption VAR_I_(0x00464EF0, INVENTORY_ITEM, "...")
#define InvGrenadeAmmoOption VAR_I_(0x00464F40, INVENTORY_ITEM, "...")
#define InvSmallMedipackOption VAR_I_(0x00464F90, INVENTORY_ITEM, "...")
#define InvLargeMedipackOption VAR_I_(0x00464FE0, INVENTORY_ITEM, "...")
#define InvPickup1Option VAR_I_(0x00465030, INVENTORY_ITEM, "...")
#define InvPickup2Option VAR_I_(0x00465080, INVENTORY_ITEM, "...")
#define InvPuzzle1Option VAR_I_(0x004650D0, INVENTORY_ITEM, "...")
#define InvPuzzle2Option VAR_I_(0x00465120, INVENTORY_ITEM, "...")
#define InvPuzzle3Option VAR_I_(0x00465170, INVENTORY_ITEM, "...")
#define InvPuzzle4Option VAR_I_(0x004651C0, INVENTORY_ITEM, "...")
#define InvKey1Option VAR_I_(0x00465210, INVENTORY_ITEM, "...")
#define InvKey2Option VAR_I_(0x00465260, INVENTORY_ITEM, "...")
#define InvKey3Option VAR_I_(0x004652B0, INVENTORY_ITEM, "...")
#define InvKey4Option VAR_I_(0x00465300, INVENTORY_ITEM, "...")
#define InvPassportOption VAR_I_(0x00465350, INVENTORY_ITEM, "...")
#define InvDetailOption VAR_I_(0x004653A0, INVENTORY_ITEM, "...")
#define InvSoundOption VAR_I_(0x004653F0, INVENTORY_ITEM, "...")
#define InvControlOption VAR_I_(0x00465440, INVENTORY_ITEM, "...")
#define InvPhotoOption VAR_I_(0x00465490, INVENTORY_ITEM, "...")
// GameFlow/Inventory uninitialized variables
#define GF_LaraStartAnim VAR_U_(0x004D77E0, int)
#define GF_SunsetEnabled VAR_U_(0x004D77E4, UINT16)
#define GF_DeadlyWater VAR_U_(0x004D77E8, UINT16)
#define GF_NoFloor VAR_U_(0x004D77EC, UINT16)
#define GF_RemoveWeapons VAR_U_(0x004D77F0, UINT16)
#define GF_RemoveAmmo VAR_U_(0x004D77F4, UINT16)
#define GF_Kill2Complete VAR_U_(0x004D77F8, bool)
#define GF_StartGame VAR_U_(0x004D77FC, bool)
#define GF_GameFlow VAR_U_(0x00521DE0, GAME_FLOW)
#define GF_ScriptBuffer VAR_U_(0x00521E70, __int16*)
#define GF_LevelNamesStringTable VAR_U_(0x00521EC4, char**)
#define GF_PictureFilesStringTable VAR_U_(0x00521EA0, char**)
#define GF_TitleFilesStringTable VAR_U_(0x00521F48, char**)
#define GF_FmvFilesStringTable VAR_U_(0x00521F94, char**)
#define GF_LevelFilesStringTable VAR_U_(0x00521F54, char**)
#define GF_CutsFilesStringTable VAR_U_(0x00521DC4, char**)
#define GF_GameStringTable VAR_U_(0x00521EB0, char**)
#define GF_SpecificStringTable VAR_U_(0x00521F50, char**)
#define GF_Puzzle1StringTable VAR_U_(0x00521DC0, char**)
#define GF_Puzzle2StringTable VAR_U_(0x00521E98, char**)
#define GF_Puzzle3StringTable VAR_U_(0x00521EC0, char**)
#define GF_Puzzle4StringTable VAR_U_(0x00521E60, char**)
#define GF_Pickup1StringTable VAR_U_(0x00521E94, char**)
#define GF_Pickup2StringTable VAR_U_(0x00521F44, char**)
#define GF_Key1StringTable VAR_U_(0x00521EA4, char**)
#define GF_Key2StringTable VAR_U_(0x00521E74, char**)
#define GF_Key3StringTable VAR_U_(0x00521EBC, char**)
#define GF_Key4StringTable VAR_U_(0x00521E90, char**)
#define GF_LevelNamesStringBuffer VAR_U_(0x00521EAC, char*)
#define GF_PictureFilesStringBuffer VAR_U_(0x00521E8C, char*)
#define GF_TitleFilesStringBuffer VAR_U_(0x00521F4C, char*)
#define GF_FmvFilesStringBuffer VAR_U_(0x00521E68, char*)
#define GF_LevelFilesStringBuffer VAR_U_(0x00521E9C, char*)
#define GF_CutsFilesStringBuffer VAR_U_(0x00521E78, char*)
#define GF_GameStringBuffer VAR_U_(0x00521EB8, char*)
#define GF_SpecificStringBuffer VAR_U_(0x00521EB4, char*)
#define GF_Puzzle1StringBuffer VAR_U_(0x00521EA8, char*)
#define GF_Puzzle2StringBuffer VAR_U_(0x00521F40, char*)
#define GF_Puzzle3StringBuffer VAR_U_(0x00521F98, char*)
#define GF_Puzzle4StringBuffer VAR_U_(0x00521F90, char*)
#define GF_Pickup1StringBuffer VAR_U_(0x00521E64, char*)
#define GF_Pickup2StringBuffer VAR_U_(0x00521E88, char*)
#define GF_Key1StringBuffer VAR_U_(0x00521E6C, char*)
#define GF_Key2StringBuffer VAR_U_(0x00521E84, char*)
#define GF_Key3StringBuffer VAR_U_(0x00521F9C, char*)
#define GF_Key4StringBuffer VAR_U_(0x00521E7C, char*)
// GameFlow/Inventory arrays
#define InvMainQtys ARRAY_(0x004654E8, UINT16, [23]) /* = {1, 1, 1, 1, 1, 1, 1, 1, 1, 0}; */
#define InvMainList ARRAY_(0x00465518, INVENTORY_ITEM*, [23]) /* = {
&InvCompassOption,
&InvFlareOption,
&InvPistolOption,
&InvShotgunOption,
&InvMagnumOption,
&InvUziOption,
&InvM16Option,
&InvGrenadeOption,
&InvHarpoonOption,
&InvLargeMedipackOption,
&InvSmallMedipackOption,
NULL,
}; */
#define InvKeysQtys ARRAY_(0x00465578, UINT16, [23]) /* = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}; */
#define InvKeysList ARRAY_(0x004655A8, INVENTORY_ITEM*, [23]) /* = {
&InvPuzzle1Option,
&InvPuzzle2Option,
&InvPuzzle3Option,
&InvPuzzle4Option,
&InvKey1Option,
&InvKey2Option,
&InvKey3Option,
&InvKey4Option,
&InvPickup1Option,
&InvPickup2Option,
NULL,
}; */
#ifdef FEATURE_HUD_IMPROVED
extern INVENTORY_ITEM *InvOptionList[5];
extern TEXT_STR_INFO *CtrlTextA[ARRAY_SIZE(Layout->key)];
extern TEXT_STR_INFO *CtrlTextB[ARRAY_SIZE(Layout->key)];
#else // FEATURE_HUD_IMPROVED
#define InvOptionList ARRAY_(0x00465608, INVENTORY_ITEM*, [4]) /* = {
&InvPassportOption,
&InvControlOption,
&InvSoundOption,
&InvPhotoOption
}; */
#define CtrlTextA ARRAY_(0x0051A248, TEXT_STR_INFO*, [14])
#define CtrlTextB ARRAY_(0x0051A280, TEXT_STR_INFO*, [14])
#endif // FEATURE_HUD_IMPROVED
#define GF_ScriptTable ARRAY_(0x00521EE0, __int16*, [24])
#define GF_DemoLevels ARRAY_(0x00521F60, UINT16, [24])
#define GF_SecretInvItems ARRAY_(0x00521FA0, char, [27])
#define GF_Add2InvItems ARRAY_(0x00521FC0, char, [27])
#endif // GLOBAL_VARS_H_INCLUDED
================================================
FILE: json-parser/LICENSE
================================================
Copyright (C) 2012, 2013 James McLaughlin et al. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
================================================
FILE: json-parser/README.md
================================================
Very low footprint JSON parser written in portable ANSI C.
* BSD licensed with no dependencies (i.e. just drop the C file into your project)
* Never recurses or allocates more memory than it needs
* Very simple API with operator sugar for C++
[](http://travis-ci.org/udp/json-parser)
_Want to serialize? Check out [json-builder](https://github.com/udp/json-builder)!_
Installing
----------
There is now a makefile which will produce a libjsonparser static and dynamic library. However, this
is _not_ required to build json-parser, and the source files (`json.c` and `json.h`) should be happy
in any build system you already have in place.
API
---
json_value * json_parse (const json_char * json,
size_t length);
json_value * json_parse_ex (json_settings * settings,
const json_char * json,
size_t length,
char * error);
void json_value_free (json_value *);
The `type` field of `json_value` is one of:
* `json_object` (see `u.object.length`, `u.object.values[x].name`, `u.object.values[x].value`)
* `json_array` (see `u.array.length`, `u.array.values`)
* `json_integer` (see `u.integer`)
* `json_double` (see `u.dbl`)
* `json_string` (see `u.string.ptr`, `u.string.length`)
* `json_boolean` (see `u.boolean`)
* `json_null`
Compile-Time Options
--------------------
-DJSON_TRACK_SOURCE
Stores the source location (line and column number) inside each `json_value`.
This is useful for application-level error reporting.
Runtime Options
---------------
settings |= json_enable_comments;
Enables C-style `// line` and `/* block */` comments.
size_t value_extra
The amount of space (if any) to allocate at the end of each `json_value`, in
order to give the application space to add metadata.
void * (* mem_alloc) (size_t, int zero, void * user_data);
void (* mem_free) (void *, void * user_data);
Custom allocator routines. If NULL, the default `malloc` and `free` will be used.
The `user_data` pointer will be forwarded from `json_settings` to allow application
context to be passed.
Changes in version 1.1.0
------------------------
* UTF-8 byte order marks are now skipped if present
* Allows cross-compilation by honoring --host if given (@wkz)
* Maximum size for error buffer is now exposed in header (@LB--)
* GCC warning for `static` after `const` fixed (@batrick)
* Optional support for C-style line and block comments added (@Jin-W-FS)
* `name_length` field added to object values
* It is now possible to retrieve the source line/column number of a parsed `json_value` when `JSON_TRACK_SOURCE` is enabled
* The application may now extend `json_value` using the `value_extra` setting
* Un-ambiguate pow call in the case of C++ overloaded pow (@fcartegnie)
* Fix null pointer de-reference when a non-existing array is closed and no root value is present
================================================
FILE: json-parser/json.c
================================================
/* vim: set et ts=3 sw=3 sts=3 ft=c:
*
* Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved.
* https://github.com/udp/json-parser
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "json.h"
#ifdef _MSC_VER
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include
#endif
const struct _json_value json_value_none;
#include
#include
#include
#include
typedef unsigned int json_uchar;
/* There has to be a better way to do this */
static const json_int_t JSON_INT_MAX = sizeof(json_int_t) == 1
? INT8_MAX
: (sizeof(json_int_t) == 2
? INT16_MAX
: (sizeof(json_int_t) == 4
? INT32_MAX
: INT64_MAX));
static unsigned char hex_value (json_char c)
{
if (isdigit(c))
return c - '0';
switch (c) {
case 'a': case 'A': return 0x0A;
case 'b': case 'B': return 0x0B;
case 'c': case 'C': return 0x0C;
case 'd': case 'D': return 0x0D;
case 'e': case 'E': return 0x0E;
case 'f': case 'F': return 0x0F;
default: return 0xFF;
}
}
static int would_overflow (json_int_t value, json_char b)
{
return ((JSON_INT_MAX - (b - '0')) / 10 ) < value;
}
typedef struct
{
unsigned long used_memory;
unsigned int uint_max;
unsigned long ulong_max;
json_settings settings;
int first_pass;
const json_char * ptr;
unsigned int cur_line, cur_col;
} json_state;
static void * default_alloc (size_t size, int zero, void * user_data)
{
return zero ? calloc (1, size) : malloc (size);
}
static void default_free (void * ptr, void * user_data)
{
free (ptr);
}
static void * json_alloc (json_state * state, unsigned long size, int zero)
{
if ((state->ulong_max - state->used_memory) < size)
return 0;
if (state->settings.max_memory
&& (state->used_memory += size) > state->settings.max_memory)
{
return 0;
}
return state->settings.mem_alloc (size, zero, state->settings.user_data);
}
static int new_value (json_state * state,
json_value ** top, json_value ** root, json_value ** alloc,
json_type type)
{
json_value * value;
int values_size;
if (!state->first_pass)
{
value = *top = *alloc;
*alloc = (*alloc)->_reserved.next_alloc;
if (!*root)
*root = value;
switch (value->type)
{
case json_array:
if (value->u.array.length == 0)
break;
if (! (value->u.array.values = (json_value **) json_alloc
(state, value->u.array.length * sizeof (json_value *), 0)) )
{
return 0;
}
value->u.array.length = 0;
break;
case json_object:
if (value->u.object.length == 0)
break;
values_size = sizeof (*value->u.object.values) * value->u.object.length;
if (! (value->u.object.values = (json_object_entry *) json_alloc
(state, values_size + ((unsigned long) value->u.object.values), 0)) )
{
return 0;
}
value->_reserved.object_mem = (*(void **) &value->u.object.values) + values_size;
value->u.object.length = 0;
break;
case json_string:
if (! (value->u.string.ptr = (json_char *) json_alloc
(state, (value->u.string.length + 1) * sizeof (json_char), 0)) )
{
return 0;
}
value->u.string.length = 0;
break;
default:
break;
};
return 1;
}
if (! (value = (json_value *) json_alloc
(state, sizeof (json_value) + state->settings.value_extra, 1)))
{
return 0;
}
if (!*root)
*root = value;
value->type = type;
value->parent = *top;
#ifdef JSON_TRACK_SOURCE
value->line = state->cur_line;
value->col = state->cur_col;
#endif
if (*alloc)
(*alloc)->_reserved.next_alloc = value;
*alloc = *top = value;
return 1;
}
#define whitespace \
case '\n': ++ state.cur_line; state.cur_col = 0; \
case ' ': case '\t': case '\r'
#define string_add(b) \
do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0);
#define line_and_col \
state.cur_line, state.cur_col
static const long
flag_next = 1 << 0,
flag_reproc = 1 << 1,
flag_need_comma = 1 << 2,
flag_seek_value = 1 << 3,
flag_escaped = 1 << 4,
flag_string = 1 << 5,
flag_need_colon = 1 << 6,
flag_done = 1 << 7,
flag_num_negative = 1 << 8,
flag_num_zero = 1 << 9,
flag_num_e = 1 << 10,
flag_num_e_got_sign = 1 << 11,
flag_num_e_negative = 1 << 12,
flag_line_comment = 1 << 13,
flag_block_comment = 1 << 14,
flag_num_got_decimal = 1 << 15;
json_value * json_parse_ex (json_settings * settings,
const json_char * json,
size_t length,
char * error_buf)
{
json_char error [json_error_max];
const json_char * end;
json_value * top, * root, * alloc = 0;
json_state state = { 0 };
long flags = 0;
double num_digits = 0, num_e = 0;
double num_fraction = 0;
/* Skip UTF-8 BOM
*/
if (length >= 3 && ((unsigned char) json [0]) == 0xEF
&& ((unsigned char) json [1]) == 0xBB
&& ((unsigned char) json [2]) == 0xBF)
{
json += 3;
length -= 3;
}
error[0] = '\0';
end = (json + length);
memcpy (&state.settings, settings, sizeof (json_settings));
if (!state.settings.mem_alloc)
state.settings.mem_alloc = default_alloc;
if (!state.settings.mem_free)
state.settings.mem_free = default_free;
memset (&state.uint_max, 0xFF, sizeof (state.uint_max));
memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max));
state.uint_max -= 8; /* limit of how much can be added before next check */
state.ulong_max -= 8;
for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass)
{
json_uchar uchar;
unsigned char uc_b1, uc_b2, uc_b3, uc_b4;
json_char * string = 0;
unsigned int string_length = 0;
top = root = 0;
flags = flag_seek_value;
state.cur_line = 1;
for (state.ptr = json ;; ++ state.ptr)
{
json_char b = (state.ptr == end ? 0 : *state.ptr);
if (flags & flag_string)
{
if (!b)
{ sprintf (error, "Unexpected EOF in string (at %d:%d)", line_and_col);
goto e_failed;
}
if (string_length > state.uint_max)
goto e_overflow;
if (flags & flag_escaped)
{
flags &= ~ flag_escaped;
switch (b)
{
case 'b': string_add ('\b'); break;
case 'f': string_add ('\f'); break;
case 'n': string_add ('\n'); break;
case 'r': string_add ('\r'); break;
case 't': string_add ('\t'); break;
case 'u':
if (end - state.ptr <= 4 ||
(uc_b1 = hex_value (*++ state.ptr)) == 0xFF ||
(uc_b2 = hex_value (*++ state.ptr)) == 0xFF ||
(uc_b3 = hex_value (*++ state.ptr)) == 0xFF ||
(uc_b4 = hex_value (*++ state.ptr)) == 0xFF)
{
sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col);
goto e_failed;
}
uc_b1 = (uc_b1 << 4) | uc_b2;
uc_b2 = (uc_b3 << 4) | uc_b4;
uchar = (uc_b1 << 8) | uc_b2;
if ((uchar & 0xF800) == 0xD800) {
json_uchar uchar2;
if (end - state.ptr <= 6 || (*++ state.ptr) != '\\' || (*++ state.ptr) != 'u' ||
(uc_b1 = hex_value (*++ state.ptr)) == 0xFF ||
(uc_b2 = hex_value (*++ state.ptr)) == 0xFF ||
(uc_b3 = hex_value (*++ state.ptr)) == 0xFF ||
(uc_b4 = hex_value (*++ state.ptr)) == 0xFF)
{
sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col);
goto e_failed;
}
uc_b1 = (uc_b1 << 4) | uc_b2;
uc_b2 = (uc_b3 << 4) | uc_b4;
uchar2 = (uc_b1 << 8) | uc_b2;
uchar = 0x010000 | ((uchar & 0x3FF) << 10) | (uchar2 & 0x3FF);
}
if (sizeof (json_char) >= sizeof (json_uchar) || (uchar <= 0x7F))
{
string_add ((json_char) uchar);
break;
}
if (uchar <= 0x7FF)
{
if (state.first_pass)
string_length += 2;
else
{ string [string_length ++] = 0xC0 | (uchar >> 6);
string [string_length ++] = 0x80 | (uchar & 0x3F);
}
break;
}
if (uchar <= 0xFFFF) {
if (state.first_pass)
string_length += 3;
else
{ string [string_length ++] = 0xE0 | (uchar >> 12);
string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F);
string [string_length ++] = 0x80 | (uchar & 0x3F);
}
break;
}
if (state.first_pass)
string_length += 4;
else
{ string [string_length ++] = 0xF0 | (uchar >> 18);
string [string_length ++] = 0x80 | ((uchar >> 12) & 0x3F);
string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F);
string [string_length ++] = 0x80 | (uchar & 0x3F);
}
break;
default:
string_add (b);
};
continue;
}
if (b == '\\')
{
flags |= flag_escaped;
continue;
}
if (b == '"')
{
if (!state.first_pass)
string [string_length] = 0;
flags &= ~ flag_string;
string = 0;
switch (top->type)
{
case json_string:
top->u.string.length = string_length;
flags |= flag_next;
break;
case json_object:
if (state.first_pass)
*(void **) &top->u.object.values += string_length + 1;
else
{
top->u.object.values [top->u.object.length].name
= (json_char *) top->_reserved.object_mem;
top->u.object.values [top->u.object.length].name_length
= string_length;
(*(json_char **) &top->_reserved.object_mem) += string_length + 1;
}
flags |= flag_seek_value | flag_need_colon;
continue;
default:
break;
};
}
else
{
string_add (b);
continue;
}
}
if (state.settings.settings & json_enable_comments)
{
if (flags & (flag_line_comment | flag_block_comment))
{
if (flags & flag_line_comment)
{
if (b == '\r' || b == '\n' || !b)
{
flags &= ~ flag_line_comment;
-- state.ptr; /* so null can be reproc'd */
}
continue;
}
if (flags & flag_block_comment)
{
if (!b)
{ sprintf (error, "%d:%d: Unexpected EOF in block comment", line_and_col);
goto e_failed;
}
if (b == '*' && state.ptr < (end - 1) && state.ptr [1] == '/')
{
flags &= ~ flag_block_comment;
++ state.ptr; /* skip closing sequence */
}
continue;
}
}
else if (b == '/')
{
if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object)
{ sprintf (error, "%d:%d: Comment not allowed here", line_and_col);
goto e_failed;
}
if (++ state.ptr == end)
{ sprintf (error, "%d:%d: EOF unexpected", line_and_col);
goto e_failed;
}
switch (b = *state.ptr)
{
case '/':
flags |= flag_line_comment;
continue;
case '*':
flags |= flag_block_comment;
continue;
default:
sprintf (error, "%d:%d: Unexpected `%c` in comment opening sequence", line_and_col, b);
goto e_failed;
};
}
}
if (flags & flag_done)
{
if (!b)
break;
switch (b)
{
whitespace:
continue;
default:
sprintf (error, "%d:%d: Trailing garbage: `%c`",
state.cur_line, state.cur_col, b);
goto e_failed;
};
}
if (flags & flag_seek_value)
{
switch (b)
{
whitespace:
continue;
case ']':
if (top && top->type == json_array)
flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next;
else
{ sprintf (error, "%d:%d: Unexpected ]", line_and_col);
goto e_failed;
}
break;
default:
if (flags & flag_need_comma)
{
if (b == ',')
{ flags &= ~ flag_need_comma;
continue;
}
else
{
sprintf (error, "%d:%d: Expected , before %c",
state.cur_line, state.cur_col, b);
goto e_failed;
}
}
if (flags & flag_need_colon)
{
if (b == ':')
{ flags &= ~ flag_need_colon;
continue;
}
else
{
sprintf (error, "%d:%d: Expected : before %c",
state.cur_line, state.cur_col, b);
goto e_failed;
}
}
flags &= ~ flag_seek_value;
switch (b)
{
case '{':
if (!new_value (&state, &top, &root, &alloc, json_object))
goto e_alloc_failure;
continue;
case '[':
if (!new_value (&state, &top, &root, &alloc, json_array))
goto e_alloc_failure;
flags |= flag_seek_value;
continue;
case '"':
if (!new_value (&state, &top, &root, &alloc, json_string))
goto e_alloc_failure;
flags |= flag_string;
string = top->u.string.ptr;
string_length = 0;
continue;
case 't':
if ((end - state.ptr) < 3 || *(++ state.ptr) != 'r' ||
*(++ state.ptr) != 'u' || *(++ state.ptr) != 'e')
{
goto e_unknown_value;
}
if (!new_value (&state, &top, &root, &alloc, json_boolean))
goto e_alloc_failure;
top->u.boolean = 1;
flags |= flag_next;
break;
case 'f':
if ((end - state.ptr) < 4 || *(++ state.ptr) != 'a' ||
*(++ state.ptr) != 'l' || *(++ state.ptr) != 's' ||
*(++ state.ptr) != 'e')
{
goto e_unknown_value;
}
if (!new_value (&state, &top, &root, &alloc, json_boolean))
goto e_alloc_failure;
flags |= flag_next;
break;
case 'n':
if ((end - state.ptr) < 3 || *(++ state.ptr) != 'u' ||
*(++ state.ptr) != 'l' || *(++ state.ptr) != 'l')
{
goto e_unknown_value;
}
if (!new_value (&state, &top, &root, &alloc, json_null))
goto e_alloc_failure;
flags |= flag_next;
break;
default:
if (isdigit (b) || b == '-')
{
if (!new_value (&state, &top, &root, &alloc, json_integer))
goto e_alloc_failure;
if (!state.first_pass)
{
while (isdigit (b) || b == '+' || b == '-'
|| b == 'e' || b == 'E' || b == '.')
{
if ( (++ state.ptr) == end)
{
b = 0;
break;
}
b = *state.ptr;
}
flags |= flag_next | flag_reproc;
break;
}
flags &= ~ (flag_num_negative | flag_num_e |
flag_num_e_got_sign | flag_num_e_negative |
flag_num_zero);
num_digits = 0;
num_fraction = 0;
num_e = 0;
if (b != '-')
{
flags |= flag_reproc;
break;
}
flags |= flag_num_negative;
continue;
}
else
{ sprintf (error, "%d:%d: Unexpected %c when seeking value", line_and_col, b);
goto e_failed;
}
};
};
}
else
{
switch (top->type)
{
case json_object:
switch (b)
{
whitespace:
continue;
case '"':
if (flags & flag_need_comma)
{ sprintf (error, "%d:%d: Expected , before \"", line_and_col);
goto e_failed;
}
flags |= flag_string;
string = (json_char *) top->_reserved.object_mem;
string_length = 0;
break;
case '}':
flags = (flags & ~ flag_need_comma) | flag_next;
break;
case ',':
if (flags & flag_need_comma)
{
flags &= ~ flag_need_comma;
break;
}
// fall through
default:
sprintf (error, "%d:%d: Unexpected `%c` in object", line_and_col, b);
goto e_failed;
};
break;
case json_integer:
case json_double:
if (isdigit (b))
{
++ num_digits;
if (top->type == json_integer || flags & flag_num_e)
{
if (! (flags & flag_num_e))
{
if (flags & flag_num_zero)
{ sprintf (error, "%d:%d: Unexpected `0` before `%c`", line_and_col, b);
goto e_failed;
}
if (num_digits == 1 && b == '0')
flags |= flag_num_zero;
}
else
{
flags |= flag_num_e_got_sign;
num_e = (num_e * 10) + (b - '0');
continue;
}
if (would_overflow(top->u.integer, b))
{ -- num_digits;
-- state.ptr;
top->type = json_double;
top->u.dbl = (double)top->u.integer;
continue;
}
top->u.integer = (top->u.integer * 10) + (b - '0');
continue;
}
if (flags & flag_num_got_decimal)
num_fraction = (num_fraction * 10) + (b - '0');
else
top->u.dbl = (top->u.dbl * 10) + (b - '0');
continue;
}
if (b == '+' || b == '-')
{
if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign))
{
flags |= flag_num_e_got_sign;
if (b == '-')
flags |= flag_num_e_negative;
continue;
}
}
else if (b == '.' && top->type == json_integer)
{
if (!num_digits)
{ sprintf (error, "%d:%d: Expected digit before `.`", line_and_col);
goto e_failed;
}
top->type = json_double;
top->u.dbl = (double) top->u.integer;
flags |= flag_num_got_decimal;
num_digits = 0;
continue;
}
if (! (flags & flag_num_e))
{
if (top->type == json_double)
{
if (!num_digits)
{ sprintf (error, "%d:%d: Expected digit after `.`", line_and_col);
goto e_failed;
}
top->u.dbl += num_fraction / pow (10.0, num_digits);
}
if (b == 'e' || b == 'E')
{
flags |= flag_num_e;
if (top->type == json_integer)
{
top->type = json_double;
top->u.dbl = (double) top->u.integer;
}
num_digits = 0;
flags &= ~ flag_num_zero;
continue;
}
}
else
{
if (!num_digits)
{ sprintf (error, "%d:%d: Expected digit after `e`", line_and_col);
goto e_failed;
}
top->u.dbl *= pow (10.0, (flags & flag_num_e_negative ? - num_e : num_e));
}
if (flags & flag_num_negative)
{
if (top->type == json_integer)
top->u.integer = - top->u.integer;
else
top->u.dbl = - top->u.dbl;
}
flags |= flag_next | flag_reproc;
break;
default:
break;
};
}
if (flags & flag_reproc)
{
flags &= ~ flag_reproc;
-- state.ptr;
}
if (flags & flag_next)
{
flags = (flags & ~ flag_next) | flag_need_comma;
if (!top->parent)
{
/* root value done */
flags |= flag_done;
continue;
}
if (top->parent->type == json_array)
flags |= flag_seek_value;
if (!state.first_pass)
{
json_value * parent = top->parent;
switch (parent->type)
{
case json_object:
parent->u.object.values
[parent->u.object.length].value = top;
break;
case json_array:
parent->u.array.values
[parent->u.array.length] = top;
break;
default:
break;
};
}
if ( (++ top->parent->u.array.length) > state.uint_max)
goto e_overflow;
top = top->parent;
continue;
}
}
alloc = root;
}
return root;
e_unknown_value:
sprintf (error, "%d:%d: Unknown value", line_and_col);
goto e_failed;
e_alloc_failure:
strcpy (error, "Memory allocation failure");
goto e_failed;
e_overflow:
sprintf (error, "%d:%d: Too long (caught overflow)", line_and_col);
goto e_failed;
e_failed:
if (error_buf)
{
if (*error)
strcpy (error_buf, error);
else
strcpy (error_buf, "Unknown error");
}
if (state.first_pass)
alloc = root;
while (alloc)
{
top = alloc->_reserved.next_alloc;
state.settings.mem_free (alloc, state.settings.user_data);
alloc = top;
}
if (!state.first_pass)
json_value_free_ex (&state.settings, root);
return 0;
}
json_value * json_parse (const json_char * json, size_t length)
{
json_settings settings = { 0 };
return json_parse_ex (&settings, json, length, 0);
}
void json_value_free_ex (json_settings * settings, json_value * value)
{
json_value * cur_value;
if (!value)
return;
value->parent = 0;
while (value)
{
switch (value->type)
{
case json_array:
if (!value->u.array.length)
{
settings->mem_free (value->u.array.values, settings->user_data);
break;
}
value = value->u.array.values [-- value->u.array.length];
continue;
case json_object:
if (!value->u.object.length)
{
settings->mem_free (value->u.object.values, settings->user_data);
break;
}
value = value->u.object.values [-- value->u.object.length].value;
continue;
case json_string:
settings->mem_free (value->u.string.ptr, settings->user_data);
break;
default:
break;
};
cur_value = value;
value = value->parent;
settings->mem_free (cur_value, settings->user_data);
}
}
void json_value_free (json_value * value)
{
json_settings settings = { 0 };
settings.mem_free = default_free;
json_value_free_ex (&settings, value);
}
================================================
FILE: json-parser/json.h
================================================
/* vim: set et ts=3 sw=3 sts=3 ft=c:
*
* Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved.
* https://github.com/udp/json-parser
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _JSON_H
#define _JSON_H
#ifndef json_char
#define json_char char
#endif
#ifndef json_int_t
#ifndef _MSC_VER
#include
#define json_int_t int64_t
#else
#define json_int_t __int64
#endif
#endif
#include
#ifdef __cplusplus
#include
extern "C"
{
#endif
typedef struct
{
unsigned long max_memory;
int settings;
/* Custom allocator support (leave null to use malloc/free)
*/
void * (* mem_alloc) (size_t, int zero, void * user_data);
void (* mem_free) (void *, void * user_data);
void * user_data; /* will be passed to mem_alloc and mem_free */
size_t value_extra; /* how much extra space to allocate for values? */
} json_settings;
#define json_enable_comments 0x01
typedef enum
{
json_none,
json_object,
json_array,
json_integer,
json_double,
json_string,
json_boolean,
json_null
} json_type;
extern const struct _json_value json_value_none;
typedef struct _json_object_entry
{
json_char * name;
unsigned int name_length;
struct _json_value * value;
} json_object_entry;
typedef struct _json_value
{
struct _json_value * parent;
json_type type;
union
{
int boolean;
json_int_t integer;
double dbl;
struct
{
unsigned int length;
json_char * ptr; /* null terminated */
} string;
struct
{
unsigned int length;
json_object_entry * values;
#if defined(__cplusplus) && __cplusplus >= 201103L
decltype(values) begin () const
{ return values;
}
decltype(values) end () const
{ return values + length;
}
#endif
} object;
struct
{
unsigned int length;
struct _json_value ** values;
#if defined(__cplusplus) && __cplusplus >= 201103L
decltype(values) begin () const
{ return values;
}
decltype(values) end () const
{ return values + length;
}
#endif
} array;
} u;
union
{
struct _json_value * next_alloc;
void * object_mem;
} _reserved;
#ifdef JSON_TRACK_SOURCE
/* Location of the value in the source JSON
*/
unsigned int line, col;
#endif
/* Some C++ operator sugar */
#ifdef __cplusplus
public:
inline _json_value ()
{ memset (this, 0, sizeof (_json_value));
}
inline const struct _json_value &operator [] (int index) const
{
if (type != json_array || index < 0
|| ((unsigned int) index) >= u.array.length)
{
return json_value_none;
}
return *u.array.values [index];
}
inline const struct _json_value &operator [] (const char * index) const
{
if (type != json_object)
return json_value_none;
for (unsigned int i = 0; i < u.object.length; ++ i)
if (!strcmp (u.object.values [i].name, index))
return *u.object.values [i].value;
return json_value_none;
}
inline operator const char * () const
{
switch (type)
{
case json_string:
return u.string.ptr;
default:
return "";
};
}
inline operator json_int_t () const
{
switch (type)
{
case json_integer:
return u.integer;
case json_double:
return (json_int_t) u.dbl;
default:
return 0;
};
}
inline operator bool () const
{
if (type != json_boolean)
return false;
return u.boolean != 0;
}
inline operator double () const
{
switch (type)
{
case json_integer:
return (double) u.integer;
case json_double:
return u.dbl;
default:
return 0;
};
}
#endif
} json_value;
json_value * json_parse (const json_char * json,
size_t length);
#define json_error_max 128
json_value * json_parse_ex (json_settings * settings,
const json_char * json,
size_t length,
char * error);
void json_value_free (json_value *);
/* Not usually necessary, unless you used a custom mem_alloc and now want to
* use a custom mem_free.
*/
void json_value_free_ex (json_settings * settings,
json_value *);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif
================================================
FILE: modding/background_new.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "modding/background_new.h"
#include "3dsystem/phd_math.h"
#include "specific/background.h"
#include "specific/file.h"
#include "specific/frontend.h"
#include "specific/hwr.h"
#include "specific/init_display.h"
#include "specific/input.h"
#include "specific/output.h"
#include "specific/texture.h"
#include "specific/winvid.h"
#include "modding/file_utils.h"
#include "modding/gdi_utils.h"
#include "modding/texture_utils.h"
#include "global/vars.h"
#ifdef FEATURE_BACKGROUND_IMPROVED
extern LPDDS CaptureBufferSurface;
extern TEXPAGE_DESC TexturePages[256];
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/joy_output.h"
#endif // FEATURE_INPUT_IMPROVED
#ifdef FEATURE_GOLD
extern bool IsGold();
#endif
static int BGND_CapturePageIndexes[64];
int BGND_TexturePageIndexes[64];
HWR_TEXHANDLE BGND_PageHandles[64];
DWORD BGND_PictureWidth = 640;
DWORD BGND_PictureHeight = 480;
bool BGND_IsCaptured = false;
DWORD PictureStretchLimit = 10;
bool RemasteredPixEnabled = true;
static DWORD BGND_TextureSide = 1024;
static DWORD BGND_TextureAlpha = 255;
/// Short wave horizontal pattern step
#define SHORT_WAVE_X_STEP (0x3000)
/// Short wave vertical pattern step
#define SHORT_WAVE_Y_STEP (0x33E7)
/// Short wave horizontal pattern offset
#define SHORT_WAVE_X_OFFSET (SHORT_WAVE_X_STEP * 2)
/// Short wave vertical pattern offset
#define SHORT_WAVE_Y_OFFSET (SHORT_WAVE_Y_STEP * 1)
/// Long wave horizontal pattern step
#define LONG_WAVE_X_STEP (0x1822)
/// Long wave vertical pattern step
#define LONG_WAVE_Y_STEP (0x1422)
/// Long wave horizontal pattern offset
#define LONG_WAVE_X_OFFSET (LONG_WAVE_X_STEP * 2)
/// Long wave vertical pattern offset
#define LONG_WAVE_Y_OFFSET (LONG_WAVE_Y_STEP * 1)
/// Pixel accuracy factor (for more exact integer computations)
#define PIXEL_ACCURACY (4)
/// Animated pattern detail level (Increases the smoothness of the curve)
#define PATTERN_DETAIL (2)
/// 2D vertex (no Z coordinate) structure
typedef struct {
D3DVALUE x; ///< Vertex X coordinate (pixels)
D3DVALUE y; ///< Vertex Y coordinate (pixels)
D3DCOLOR color; ///< Vertex color (ARGB)
} VERTEX2D;
/// Texture data structure
typedef struct {
HWR_TEXHANDLE handle; ///< Handle of texture
int u; ///< Texture U coordinate (pixels)
int v; ///< Texture V coordinate (pixels)
int width; ///< Texture width (pixels)
int height; ///< Texture height (pixels)
} TEXTURE;
/**
* Draws flat textured quad polygon (two triangles) at far Z coordinate
* @param[in] vtx0,vtx1,vtx2,vtx3 Pointers to the Vertex structures
* @param[in] txr Pointer to the Texture structure
*/
void RenderTexturedFarQuad(VERTEX2D *vtx0, VERTEX2D *vtx1, VERTEX2D *vtx2, VERTEX2D *vtx3, TEXTURE *txr) {
extern int PatternTexPage;
D3DTLVERTEX vtx[4];
float tu0 = (double)txr->u / 256.0;
float tv0 = (double)txr->v / 256.0;
float tu1 = (double)(txr->u + txr->width) / 256.0;
float tv1 = (double)(txr->v + txr->height) / 256.0;
if( PatternTexPage < 0 ) {
double uvAdjust = (double)UvAdd / (double)(256 * GetTextureSideByHandle(txr->handle));
CLAMPL(uvAdjust, 1.0/double(PHD_ONE));
tu0 += uvAdjust;
tv0 += uvAdjust;
tu1 -= uvAdjust;
tv1 -= uvAdjust;
}
float rhw = RhwFactor / FltFarZ;
vtx[0].sx = vtx0->x;
vtx[0].sy = vtx0->y;
vtx[0].color = vtx0->color;
vtx[0].tu = tu0;
vtx[0].tv = tv0;
vtx[1].sx = vtx1->x;
vtx[1].sy = vtx1->y;
vtx[1].color = vtx1->color;
vtx[1].tu = tu1;
vtx[1].tv = tv0;
vtx[2].sx = vtx2->x;
vtx[2].sy = vtx2->y;
vtx[2].color = vtx2->color;
vtx[2].tu = tu0;
vtx[2].tv = tv1;
vtx[3].sx = vtx3->x;
vtx[3].sy = vtx3->y;
vtx[3].color = vtx3->color;
vtx[3].tu = tu1;
vtx[3].tv = tv1;
for( int i=0; i<4; ++i ) {
vtx[i].sz = 0.995;
vtx[i].rhw = rhw;
vtx[i].specular = 0;
}
HWR_TexSource(txr->handle);
HWR_EnableColorKey(false);
HWR_DrawPrimitive(D3DPT_TRIANGLESTRIP, &vtx, 4, false);
}
/**
* Draws animated pattern wallpaper (TR2 PlayStation inventory style)
* @param[in] texSource Handle of texture
* @param[in] tu Texture U coordinate (pixels)
* @param[in] tv Texture V coordinate (pixels)
* @param[in] t_width Texture width (pixels)
* @param[in] t_height Texture height (pixels)
* @param[in] halfRowCount Half number of vertical rows of the wallpaper pattern
* @param[in] amplitude Percent value of the deformation amplitude (vertex rotation radius)
* @param[in] deformWavePhase Deformation wave phase in Integer representation
* @param[in] shortWavePhase Lighting short wave phase in Integer representation
* @param[in] longWavePhase Lighting long wave phase in Integer representation
*/
void PSX_Background(HWR_TEXHANDLE texSource, int tu, int tv, int t_width, int t_height, int halfRowCount,
__int16 amplitude, __int16 deformWavePhase, __int16 shortWavePhase, __int16 longWavePhase)
{
int halfColCount = MulDiv(halfRowCount, PhdWinWidth*3, PhdWinHeight*4) + 1;
halfRowCount *= PATTERN_DETAIL;
halfColCount *= PATTERN_DETAIL;
int light;
int countY = halfRowCount*2+1;
int countX = halfColCount*2+1;
int tileSize = MulDiv(PhdWinHeight, PIXEL_ACCURACY*2, halfRowCount*3);
int tileRadius = MulDiv(tileSize, amplitude*PATTERN_DETAIL, 100);
int baseY = PhdWinHeight * PIXEL_ACCURACY/2 - halfRowCount*tileSize;
int baseX = PhdWinWidth * PIXEL_ACCURACY/2 - halfColCount*tileSize;
VERTEX2D *vertices = (VERTEX2D *)malloc(sizeof(VERTEX2D)*countX*countY);
TEXTURE subTxr;
deformWavePhase += SHORT_WAVE_X_OFFSET;
shortWavePhase += SHORT_WAVE_X_OFFSET;
longWavePhase += LONG_WAVE_X_OFFSET;
for( int i=0; i= 0x900)
if( SavedAppSettings.LightingMode ) light /= 2;
#endif // defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
vtx->color = RGBA_MAKE(light, light, light, 0xFFu);
vtx->y = ((float)(baseY + tileSize*j + phd_sin(deformWaveRowPhase)*tileRadius/0x4000)) / PIXEL_ACCURACY;
vtx->x = ((float)(baseX + tileSize*i + phd_cos(deformWaveRowPhase)*tileRadius/0x4000)) / PIXEL_ACCURACY;
deformWaveRowPhase += SHORT_WAVE_Y_STEP / PATTERN_DETAIL;
shortWaveRowPhase += SHORT_WAVE_Y_STEP / PATTERN_DETAIL;
longWaveRowPhase += LONG_WAVE_Y_STEP / PATTERN_DETAIL;
}
deformWavePhase += SHORT_WAVE_X_STEP / PATTERN_DETAIL;
shortWavePhase += SHORT_WAVE_X_STEP / PATTERN_DETAIL;
longWavePhase += LONG_WAVE_X_STEP / PATTERN_DETAIL;
}
subTxr.handle = texSource;
subTxr.width = t_width / PATTERN_DETAIL;
subTxr.height = t_height / PATTERN_DETAIL;
for( int i=0; i= 0 && TexturePages[pageIndex].width != side ) {
SafeFreeTexturePage(pageIndex);
pageIndex = -1;
}
if( pageIndex < 0 || !CHK_ANY(TexturePages[pageIndex].status, 1) ) {
DDSDESC desc;
#if (DIRECT3D_VERSION >= 0x900)
pageIndex = CreateTexturePage(side, side, false);
if( pageIndex < 0 ) {
return -1;
}
if SUCCEEDED(TexturePages[pageIndex].texture->LockRect(0, &desc, NULL, 0)) {
TexturePages[pageIndex].texture->UnlockRect(0);
}
#else // (DIRECT3D_VERSION >= 0x900)
pageIndex = CreateTexturePage(side, side, NULL);
if SUCCEEDED(WinVidBufferLock(TexturePages[pageIndex].sysMemSurface, &desc, DDLOCK_WRITEONLY|DDLOCK_WAIT)) {
WinVidBufferUnlock(TexturePages[pageIndex].sysMemSurface, &desc);
}
#endif // (DIRECT3D_VERSION >= 0x900)
if( pageIndex < 0 ) {
return -1;
}
BGND_CapturePageIndexes[index] = pageIndex;
}
return pageIndex;
}
void BGND2_CleanupCaptureTextures() {
for( DWORD i=0; i= ARRAY_SIZE(BGND_TexturePageIndexes) ) {
return -1; // It seems image is too big
}
for( DWORD j = 0; j < ny; ++j ) {
for( DWORD i = 0; i < nx; ++i ) {
DWORD w = side;
DWORD h = side;
if( i == nx - 1 && width % side ) w = width % side;
if( j == ny - 1 && height % side ) h = height % side;
int pageIndex = MakeCustomTexture(i*side, j*side, w, h, width, side, bpp, bitmap, bmpPal, BGND_PaletteIndex, NULL, false);
if( pageIndex < 0) {
return -1;
}
BGND_TexturePageIndexes[i + j*nx] = pageIndex;
}
}
BGND_PictureWidth = width;
BGND_PictureHeight = height;
BGND_TextureSide = side;
BGND_GetPageHandles();
BGND_PictureIsReady = true;
return 0;
}
static int PickBestPictureFile(LPTSTR fileName, LPCTSTR modDir) {
static const STRING_FIXED4 exts[] = {"PNG", "JPG", "BMP"};
static const BYTE numAspects = 7;
static const BYTE aspects[numAspects][2] = {
{5,4}, {4,3}, {3,2}, {16,10}, {16,9}, {21,9}, {32,9} // common display aspect ratios
};
if( fileName == NULL || !*fileName || modDir == NULL || !*modDir ) {
return -1;
}
DWORD i, j;
float winAspect = (float)PhdWinWidth / (float)PhdWinHeight;
float stretch[numAspects];
BYTE tmp, idx[numAspects];
for( i = 0; i < numAspects; ++i ) {
float imgAspect = (float)aspects[i][0] / (float)aspects[i][1];
idx[i] = i;
stretch[i] = (imgAspect > winAspect) ? (imgAspect / winAspect) : (winAspect / imgAspect);
}
for( i = 0; i < (numAspects - 1); ++i ) {
for( j = (i + 1); j < numAspects; ++j ) {
if( stretch[idx[i]] > stretch[idx[j]] ) {
SWAP(idx[i], idx[j], tmp); // sort stretch values
}
}
}
bool isRemasterEnabled = RemasteredPixEnabled && SavedAppSettings.RenderMode == RM_Hardware && TextureFormat.bpp >= 16;
char altPath[256];
for( i = 0; i < numAspects; ++i ) {
snprintf(altPath, sizeof(altPath), ".\\%s\\%dx%d", modDir, aspects[idx[i]][0], aspects[idx[i]][1]);
if( 0 < AutoSelectPathAndExtension(fileName, altPath, exts, isRemasterEnabled ? ARRAY_SIZE(exts) : 0) ) {
return 2;
}
}
snprintf(altPath, sizeof(altPath), ".\\%s", modDir);
return AutoSelectPathAndExtension(fileName, altPath, exts, isRemasterEnabled ? ARRAY_SIZE(exts) : 0);
}
int __cdecl BGND2_FadeTo(int target, int delta) {
int current = BGND_TextureAlpha;
if( target > current && delta > 0 ) {
current += delta;
CLAMPG(current, target);
} else if( target < current && delta < 0 ) {
current += delta;
CLAMPL(current, target);
} else {
return current;
}
CLAMP(current, 0, 255);
BGND_TextureAlpha = current;
return current;
}
static int __cdecl BGND2_FadeToPal(int fadeValue, RGB888 *palette, int inputCheck) {
int i, j;
int palStartIdx = 0;
int palEndIdx = 256;
#if (DIRECT3D_VERSION < 0x900)
int palSize = 256;
#endif // (DIRECT3D_VERSION < 0x900)
bool fadeFaster = false;
PALETTEENTRY fadePal[256];
#if (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.RenderMode != RM_Software )
return fadeValue;
#else // (DIRECT3D_VERSION >= 0x900)
if( !GameVid_IsVga )
return fadeValue;
if( GameVid_IsWindowedVga ) {
palStartIdx += 10;
palEndIdx -= 10;
palSize -= 20;
}
#endif // (DIRECT3D_VERSION >= 0x900)
if( fadeValue <= 1 ) {
for( i=palStartIdx; i= 0x900)
S_InitialisePolyList(FALSE);
S_OutputPolyList();
#else // (DIRECT3D_VERSION >= 0x900)
DDrawPalette->SetEntries(0, palStartIdx, palSize, &WinVidPalette[palStartIdx]);
#endif // (DIRECT3D_VERSION >= 0x900)
return fadeValue;
}
for( i=palStartIdx; i= 0x900)
S_InitialisePolyList(FALSE);
S_OutputPolyList();
#else // (DIRECT3D_VERSION >= 0x900)
DDrawPalette->SetEntries(0, palStartIdx, palSize, &WinVidPalette[palStartIdx]);
#endif // (DIRECT3D_VERSION >= 0x900)
S_DumpScreen();
}
return fadeFaster ? 1 : 0;
}
static void BGND2_CustomBlt(LPDDSDESC dst, DWORD dstX, DWORD dstY, LPDDSDESC src, LPRECT srcRect) {
DWORD srcX = srcRect->left;
DWORD srcY = srcRect->top;
DWORD width = srcRect->right - srcRect->left;
DWORD height = srcRect->bottom - srcRect->top;
#if (DIRECT3D_VERSION >= 0x900)
BYTE *srcLine = (BYTE *)src->pBits + srcY * src->Pitch + srcX * 4;
BYTE *dstLine = (BYTE *)dst->pBits + dstY * dst->Pitch + dstX * 4;
for( DWORD j = 0; j < height; ++j ) {
memcpy(dstLine, srcLine, sizeof(DWORD) * width);
srcLine += src->Pitch;
dstLine += dst->Pitch;
}
#else // (DIRECT3D_VERSION >= 0x900)
DWORD srcBpp = src->ddpfPixelFormat.dwRGBBitCount/8;
DWORD dstBpp = dst->ddpfPixelFormat.dwRGBBitCount/8;
COLOR_BIT_MASKS srcMask, dstMask;
WinVidGetColorBitMasks(&srcMask, &src->ddpfPixelFormat);
WinVidGetColorBitMasks(&dstMask, &dst->ddpfPixelFormat);
BYTE *srcLine = (BYTE *)src->lpSurface + srcY * src->lPitch + srcX * srcBpp;
BYTE *dstLine = (BYTE *)dst->lpSurface + dstY * dst->lPitch + dstX * dstBpp;
for( DWORD j = 0; j < height; ++j ) {
BYTE *srcPtr = srcLine;
BYTE *dstPtr = dstLine;
for( DWORD i = 0; i < width; ++i ) {
DWORD color = 0;
memcpy(&color, srcPtr, srcBpp);
DWORD red = ((color & srcMask.dwRBitMask) >> srcMask.dwRBitOffset);
DWORD green = ((color & srcMask.dwGBitMask) >> srcMask.dwGBitOffset);
DWORD blue = ((color & srcMask.dwBBitMask) >> srcMask.dwBBitOffset);
if( srcMask.dwRBitDepth < dstMask.dwRBitDepth ) {
DWORD high = dstMask.dwRBitDepth - srcMask.dwRBitDepth;
DWORD low = (srcMask.dwRBitDepth > high) ? srcMask.dwRBitDepth - high : 0;
red = (red << high) | (red >> low);
} else if( srcMask.dwRBitDepth > dstMask.dwRBitDepth ) {
red >>= srcMask.dwRBitDepth - dstMask.dwRBitDepth;
}
if( srcMask.dwGBitDepth < dstMask.dwGBitDepth ) {
DWORD high = dstMask.dwGBitDepth - srcMask.dwGBitDepth;
DWORD low = (srcMask.dwGBitDepth > high) ? srcMask.dwGBitDepth - high : 0;
green = (green << high) | (green >> low);
} else if( srcMask.dwGBitDepth > dstMask.dwGBitDepth ) {
green >>= srcMask.dwGBitDepth - dstMask.dwGBitDepth;
}
if( srcMask.dwBBitDepth < dstMask.dwBBitDepth ) {
DWORD high = dstMask.dwBBitDepth - srcMask.dwBBitDepth;
DWORD low = (srcMask.dwBBitDepth > high) ? srcMask.dwBBitDepth - high : 0;
blue = (blue << high) | (blue >> low);
} else if( srcMask.dwBBitDepth > dstMask.dwBBitDepth ) {
blue >>= srcMask.dwBBitDepth - dstMask.dwBBitDepth;
}
color = dst->ddpfPixelFormat.dwRGBAlphaBitMask; // destination is opaque
color |= red << dstMask.dwRBitOffset;
color |= green << dstMask.dwGBitOffset;
color |= blue << dstMask.dwBBitOffset;
memcpy(dstPtr, &color, dstBpp);
srcPtr += srcBpp;
dstPtr += dstBpp;
}
srcLine += src->lPitch;
dstLine += dst->lPitch;
}
#endif // (DIRECT3D_VERSION >= 0x900)
}
int __cdecl BGND2_CapturePicture() {
bool isSrcLock = false;
int ret = 0;
DDSDESC srcDesc, dstDesc;
DWORD width = 0;
DWORD height = 0;
RECT rect = {0, 0, 0, 0};
if( SavedAppSettings.RenderMode != RM_Hardware || TextureFormat.bpp < 16 ) {
return -1;
}
BGND_PictureIsReady = false;
BGND_IsCaptured = false;
// do game window capture, not the whole screen
if( !GetClientRect(HGameWindow, &rect) ) {
return -1;
}
#if (DIRECT3D_VERSION < 0x900)
LPDDS surface = CaptureBufferSurface ? CaptureBufferSurface : PrimaryBufferSurface;
#endif // (DIRECT3D_VERSION < 0x900)
if( CaptureBufferSurface == NULL ) {
MapWindowPoints(HGameWindow, GetParent(HGameWindow), (LPPOINT)&rect, 2);
}
width = ABS(rect.right - rect.left);
height = ABS(rect.bottom - rect.top);
DWORD side = MIN(2048, GetMaxTextureSize());
DWORD nx = (width + side - 1) / side;
DWORD ny = (height + side - 1) / side;
if( nx*ny >= ARRAY_SIZE(BGND_CapturePageIndexes) ) {
return -1; // It seems image is too big
}
int x[ARRAY_SIZE(BGND_CapturePageIndexes) + 1];
x[nx] = rect.right - rect.left;
for( DWORD i = 0; i < nx; ++i ) {
x[i] = i * side * x[nx] / width;
}
int y[ARRAY_SIZE(BGND_CapturePageIndexes) + 1];
y[ny] = rect.bottom - rect.top;
for( DWORD i = 0; i < ny; ++i ) {
y[i] = i * side * y[ny] / height;
}
#if (DIRECT3D_VERSION >= 0x900)
DISPLAY_MODE mode;
LPDDS surface = NULL;
if( CaptureBufferSurface != NULL ) {
surface = CaptureBufferSurface;
} else if( !WinVidGetDisplayMode(&mode)
|| FAILED(D3DDev->CreateOffscreenPlainSurface(mode.width, mode.height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, NULL))
|| FAILED(D3DDev->GetFrontBufferData(0, surface)) )
{
ret = -1;
goto CLEANUP;
}
#endif // (DIRECT3D_VERSION >= 0x900)
for( DWORD j = 0; j < ny; ++j ) {
for( DWORD i = 0; i < nx; ++i ) {
RECT r = {x[i], y[j], x[i+1], y[j+1]};
int pageIndex = CreateCaptureTexture(i + j*nx, side);
if( pageIndex < 0 ) {
ret = -1;
goto CLEANUP;
}
// NOTE: On some system default Blt/BltFast is unsupported here but returns no error, so failsafe custom blitter is used instead
if( !isSrcLock ) {
HRESULT rc;
memset(&srcDesc, 0, sizeof(srcDesc));
#if (DIRECT3D_VERSION >= 0x900)
rc = surface->LockRect(&srcDesc, &rect, D3DLOCK_READONLY);
#else // (DIRECT3D_VERSION >= 0x900)
srcDesc.dwSize = sizeof(srcDesc);
do {
rc = surface->Lock(&rect, &srcDesc, DDLOCK_READONLY|DDLOCK_WAIT, NULL);
} while( rc == DDERR_WASSTILLDRAWING );
if( rc == DDERR_SURFACELOST ) {
rc = surface->Restore();
}
#endif // (DIRECT3D_VERSION >= 0x900)
if FAILED(rc) {
ret = -1;
goto CLEANUP;
}
isSrcLock = true;
}
#if (DIRECT3D_VERSION >= 0x900)
if FAILED(TexturePages[pageIndex].texture->LockRect(0, &dstDesc, NULL, 0)) {
ret = -1;
goto CLEANUP;
}
BGND2_CustomBlt(&dstDesc, 0, 0, &srcDesc, &r);
TexturePages[pageIndex].texture->UnlockRect(0);
#else // (DIRECT3D_VERSION >= 0x900)
if FAILED(WinVidBufferLock(TexturePages[pageIndex].sysMemSurface, &dstDesc, DDLOCK_WRITEONLY|DDLOCK_WAIT)) {
ret = -1;
goto CLEANUP;
}
BGND2_CustomBlt(&dstDesc, 0, 0, &srcDesc, &r);
WinVidBufferUnlock(TexturePages[pageIndex].sysMemSurface, &dstDesc);
if( !LoadTexturePage(pageIndex, false) ) {
ret = -1;
goto CLEANUP;
}
#endif // (DIRECT3D_VERSION >= 0x900)
}
}
BGND_TextureSide = side;
BGND_TextureAlpha = 255;
BGND_PictureWidth = width;
BGND_PictureHeight = height;
BGND_PictureIsReady = true;
BGND_IsCaptured = true;
CLEANUP :
#if (DIRECT3D_VERSION >= 0x900)
if( surface != NULL ) {
if( isSrcLock ) {
surface->UnlockRect();
}
if( surface != CaptureBufferSurface ) {
surface->Release();
}
}
#else // (DIRECT3D_VERSION >= 0x900)
if( isSrcLock ) {
surface->Unlock(srcDesc.lpSurface);
}
#endif // (DIRECT3D_VERSION >= 0x900)
return ret;
}
int __cdecl BGND2_LoadPicture(LPCTSTR fileName, BOOL isTitle, BOOL isReload) {
static char lastFileName[256] = {0};
static char lastFullPath[256] = {0};
static int lastWinWidth = 0;
static int lastWinHeight = 0;
static BOOL lastTitleState = 0;
DWORD bytesRead;
HANDLE hFile;
DWORD fileSize, bitmapSize;
BYTE *fileData = NULL;
BYTE *bitmapData = NULL;
DWORD width, height, bpp = 8;
char fullPath[256] = {0};
bool isPCX;
int pickResult = -1;
BGND_IsCaptured = false; // captured screen is not valid since we want picture file now
if( isReload ) {
if( IsGameWindowChanging || IsGameWindowUpdating ||
(lastWinWidth == PhdWinWidth && lastWinHeight == PhdWinHeight) )
{
return 0; // same dimensions - no need to reload picture
}
fileName = lastFileName; // assign last fileName pointer as parameter
isTitle = lastTitleState; // copy last isTitle state
} else {
BGND_TextureAlpha = 255;
if( fileName == NULL || *fileName == 0 ) {
goto FAIL;
}
strncpy(lastFileName, fileName, sizeof(lastFileName)-1); // backup filename string
lastTitleState = isTitle; // backup isTitle state
}
lastWinWidth = PhdWinWidth;
lastWinHeight = PhdWinHeight;
#ifdef FEATURE_GOLD
if( IsGold() ) {
AddFilenameSuffix(fullPath, sizeof(fullPath), GetFullPath(fileName), "g");
pickResult = PickBestPictureFile(fullPath, "pix");
}
if( !IsGold() || pickResult < 0 ) {
strncpy(fullPath, GetFullPath(fileName), sizeof(fullPath)-1);
pickResult = PickBestPictureFile(fullPath, "pix");
}
#else // !FEATURE_GOLD
strncpy(fullPath, GetFullPath(fileName), sizeof(fullPath));
pickResult = PickBestPictureFile(fullPath, "pix");
#endif // FEATURE_GOLD
if( pickResult < 0 ) {
if( isReload ) {
if( !strncmp(lastFullPath, fullPath, sizeof(lastFullPath)) ) {
return 0; // same filepath - no need to reload picture
}
strncpy(lastFullPath, fullPath, sizeof(lastFullPath));
} else {
goto FAIL;
}
}
if( INVALID_FILE_ATTRIBUTES == GetFileAttributes(fullPath) ) {
goto FAIL;
}
if( !stricmp(PathFindExtension(fullPath), ".pcx") ) {
hFile = CreateFile(fullPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE ) {
goto FAIL;
}
fileSize = GetFileSize(hFile, NULL);
fileData = (BYTE *)malloc(fileSize);
ReadFile(hFile, fileData, fileSize, &bytesRead, NULL);
CloseHandle(hFile);
if( GetPcxResolution(fileData, fileSize, &width, &height) ) {
goto FAIL;
}
bitmapSize = width * height;
bitmapData = (BYTE *)malloc(bitmapSize);
DecompPCX(fileData, fileSize, bitmapData, PicPalette);
isPCX = true;
} else if( SavedAppSettings.RenderMode == RM_Hardware && TextureFormat.bpp >= 16 ) {
#if (DIRECT3D_VERSION >= 0x900)
bpp = 32;
#else // (DIRECT3D_VERSION >= 0x900)
bpp = 16;
#endif // (DIRECT3D_VERSION >= 0x900)
if( GDI_LoadImageFile(fullPath, &bitmapData, &width, &height, bpp) ) {
goto FAIL;
}
bitmapSize = width * height * 2;
isPCX = false;
} else {
goto FAIL;
}
#if (DIRECT3D_VERSION >= 0x900)
if( PictureBuffer.bitmap == NULL ||
PictureBuffer.width != width ||
PictureBuffer.height != height )
{
BGND_PictureWidth = width;
BGND_PictureHeight = height;
try {
CreatePictureBuffer();
} catch(...) {
goto FAIL;
}
}
#else // (DIRECT3D_VERSION >= 0x900)
if( PictureBufferSurface != NULL &&
(BGND_PictureWidth != width || BGND_PictureHeight != height) )
{
BGND_PictureWidth = width;
BGND_PictureHeight = height;
PictureBufferSurface->Release();
PictureBufferSurface = NULL;
}
if( PictureBufferSurface == NULL ) {
try {
CreatePictureBuffer();
} catch(...) {
goto FAIL;
}
}
#endif // (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.RenderMode == RM_Software ) {
#if (DIRECT3D_VERSION >= 0x900)
if( PictureBuffer.bitmap != NULL)
memcpy(PictureBuffer.bitmap, bitmapData, PictureBuffer.width * PictureBuffer.height);
#else // (DIRECT3D_VERSION >= 0x900)
WinVidCopyBitmapToBuffer(PictureBufferSurface, bitmapData);
#endif // (DIRECT3D_VERSION >= 0x900)
} else {
MakeBgndTextures(width, height, bpp, bitmapData, isPCX ? PicPalette : NULL);
}
if( !isTitle && isPCX ) {
#if (DIRECT3D_VERSION >= 0x900)
memcpy(GamePalette8, PicPalette, sizeof(GamePalette8));
#else // (DIRECT3D_VERSION >= 0x900)
CopyBitmapPalette(PicPalette, bitmapData, bitmapSize, GamePalette8);
#endif // (DIRECT3D_VERSION >= 0x900)
}
if( bitmapData != NULL ) {
free(bitmapData);
}
if( fileData != NULL ) {
free(fileData);
}
return 0;
FAIL :
#if (DIRECT3D_VERSION >= 0x900)
if( PictureBuffer.bitmap != NULL ) {
free(PictureBuffer.bitmap);
PictureBuffer.bitmap = NULL;
}
#else // (DIRECT3D_VERSION >= 0x900)
if( PictureBufferSurface != NULL ) {
PictureBufferSurface->Release();
PictureBufferSurface = NULL;
}
#endif // (DIRECT3D_VERSION >= 0x900)
if( bitmapData != NULL ) {
free(bitmapData);
}
if( fileData != NULL ) {
free(fileData);
}
S_DontDisplayPicture();
return -1;
}
int __cdecl BGND2_ShowPicture(DWORD fadeIn, DWORD waitIn, DWORD fadeOut, DWORD waitOut, BOOL inputCheck) {
if( SavedAppSettings.RenderMode == RM_Software ) {
int skip = 0;
RGB888 blackPal[256];
memset(blackPal, 0, sizeof(blackPal));
// output picture
S_InitialisePolyList(FALSE);
S_CopyBufferToScreen();
S_OutputPolyList();
// fade in
if( fadeIn > 0 ) {
memset(WinVidPalette, 0, sizeof(WinVidPalette));
skip = BGND2_FadeToPal(fadeIn, GamePalette8, inputCheck ? 1 : 0);
}
if( IsGameToExit ) {
return -1;
}
if( skip ) {
inputCheck = FALSE;
fadeOut /= 2;
waitOut /= 2;
} else if( waitIn > 0 ) {
S_Wait(waitIn * TICKS_PER_FRAME, inputCheck);
}
if( IsGameToExit ) {
return -1;
}
// fade out
if( fadeOut > 0 ) {
skip = BGND2_FadeToPal(fadeOut, blackPal, inputCheck ? 2 : 0);
if( skip ) waitOut /= 2;
}
if( IsGameToExit ) {
return -1;
}
if( fadeOut > 0 || waitOut > 0 ) {
#if (DIRECT3D_VERSION >= 0x900)
ScreenClear(false);
S_Wait(2 * TICKS_PER_FRAME, FALSE);
#else // (DIRECT3D_VERSION >= 0x900)
ScreenClear(false); ScreenDump();
ScreenClear(false); ScreenDump();
#endif // (DIRECT3D_VERSION >= 0x900)
}
if( waitOut > 2 ) {
S_Wait((waitOut - 2) * TICKS_PER_FRAME, inputCheck);
}
if( IsGameToExit ) {
return -1;
}
} else {
DWORD i = fadeIn + waitIn + fadeOut + waitOut;
DWORD phase = 0;
DWORD frame = 0;
BGND_TextureAlpha = 255;
while( i-- ) {
switch( phase ) {
case 0 :
if( frame < fadeIn ) {
BGND_TextureAlpha = 255 * frame / (fadeIn - 1);
break;
}
++phase;
frame = 0;
// fall through
case 1 :
if( frame < waitIn ) {
BGND_TextureAlpha = 255;
break;
}
++phase;
frame = 0;
// fall through
case 2 :
if( frame < fadeOut ) {
BGND_TextureAlpha = 255 * (fadeOut - frame - 1) / fadeOut;
break;
}
++phase;
frame = 0;
// fall through
case 3 :
if( frame < waitOut ) {
BGND_TextureAlpha = 0;
break;
}
++phase;
frame = 0;
// fall through
default :
return 0;
}
S_InitialisePolyList(FALSE);
S_CopyBufferToScreen();
S_OutputPolyList();
S_DumpScreen();
S_UpdateInput();
if( IsGameToExit ) {
return -1;
}
if( inputCheck && InputStatus != 0 ) {
// fade out faster if key pressed
inputCheck = FALSE;
fadeOut /= 2;
waitOut /= 2;
phase = 2;
for( frame = 0; frame < fadeOut; ++frame ) {
if( BGND_TextureAlpha > 255 * (fadeOut - frame - 1) / fadeOut ) {
break;
}
}
}
++frame;
#ifdef FEATURE_INPUT_IMPROVED
UpdateJoyOutput(false);
#endif // FEATURE_INPUT_IMPROVED
}
}
return 0;
}
static void __cdecl BGND2_DrawTexture(RECT *rect, HWR_TEXHANDLE texSource,
int tu, int tv, int t_width, int t_height, int t_side,
D3DCOLOR color0, D3DCOLOR color1, D3DCOLOR color2, D3DCOLOR color3)
{
float sx0, sy0, sx1, sy1;
float tu0, tv0, tu1, tv1;
DWORD alphaState;
D3DTLVERTEX vertex[4];
if( rect == NULL ) {
return;
}
sx0 = (double)rect->left;
sy0 = (double)rect->top;
sx1 = (double)rect->right;
sy1 = (double)rect->bottom;
tu0 = (double)tu / (double)t_side;
tv0 = (double)tv / (double)t_side;
tu1 = (double)(tu + t_width) / (double)t_side;
tv1 = (double)(tv + t_height) / (double)t_side;
vertex[0].sx = sx0;
vertex[0].sy = sy0;
vertex[0].color = RGBA_SETALPHA(color0, BGND_TextureAlpha);
vertex[0].tu = tu0;
vertex[0].tv = tv0;
vertex[1].sx = sx1;
vertex[1].sy = sy0;
vertex[1].color = RGBA_SETALPHA(color1, BGND_TextureAlpha);
vertex[1].tu = tu1;
vertex[1].tv = tv0;
vertex[2].sx = sx0;
vertex[2].sy = sy1;
vertex[2].color = RGBA_SETALPHA(color2, BGND_TextureAlpha);
vertex[2].tu = tu0;
vertex[2].tv = tv1;
vertex[3].sx = sx1;
vertex[3].sy = sy1;
vertex[3].color = RGBA_SETALPHA(color3, BGND_TextureAlpha);
vertex[3].tu = tu1;
vertex[3].tv = tv1;
for( int i=0; i<4; ++i ) {
vertex[i].sz = 0.995;
vertex[i].rhw = RhwFactor / FltFarZ;
vertex[i].specular = 0;
}
HWR_TexSource(texSource);
HWR_EnableColorKey(false);
D3DDev->GetRenderState(AlphaBlendEnabler, &alphaState);
D3DDev->SetRenderState(AlphaBlendEnabler, TRUE);
HWR_DrawPrimitive(D3DPT_TRIANGLESTRIP, &vertex, 4, true);
D3DDev->SetRenderState(AlphaBlendEnabler, alphaState);
}
void __cdecl BGND2_DrawTextures(RECT *rect, D3DCOLOR color) {
if( !BGND_PictureIsReady || !BGND_TextureSide ) {
return;
}
DWORD width = BGND_PictureWidth;
DWORD height = BGND_PictureHeight;
DWORD side = BGND_TextureSide;
DWORD nx = (width + side - 1) / side;
DWORD ny = (height + side - 1) / side;
if( nx*ny >= ARRAY_SIZE(BGND_TexturePageIndexes) ) {
return; // It seems image is too big
}
int x[ARRAY_SIZE(BGND_TexturePageIndexes) + 1];
for( DWORD i = 0; i < nx; ++i ) {
x[i] = i * side * (rect->right - rect->left) / width + rect->left;
}
x[nx] = rect->right;
int y[ARRAY_SIZE(BGND_TexturePageIndexes) + 1];
for( DWORD i = 0; i < ny; ++i ) {
y[i] = i * side * (rect->bottom - rect->top) / height + rect->top;
}
y[ny] = rect->bottom;
for( DWORD j = 0; j < ny; ++j ) {
for( DWORD i = 0; i < nx; ++i ) {
DWORD w = side;
DWORD h = side;
if( i == nx - 1 && width % side ) w = width % side;
if( j == ny - 1 && height % side ) h = height % side;
RECT r = {x[i], y[j], x[i+1], y[j+1]};
int *index = BGND_IsCaptured ? BGND_CapturePageIndexes : BGND_TexturePageIndexes;
BGND2_DrawTexture(&r, GetTexturePageHandle(index[i + j*nx]), 0, 0, w, h, side, color, color, color, color);
}
}
}
int __cdecl BGND2_CalculatePictureRect(RECT *rect) {
if( rect == NULL || !PhdWinWidth || !PhdWinHeight || !BGND_PictureWidth || !BGND_PictureHeight )
return -1;
if( PictureStretchLimit >= 100 ) {
*rect = PhdWinRect;
return 1;
}
DWORD stretch;
int x, y, w, h;
double windowAspect = (double)PhdWinWidth / (double)PhdWinHeight;
double pictureAspect = (double)BGND_PictureWidth / (double)BGND_PictureHeight;
if( windowAspect > pictureAspect ) {
w = (double)PhdWinHeight * pictureAspect;
h = PhdWinHeight;
x = (PhdWinWidth - w) / 2;
y = 0;
stretch = 100 * PhdWinWidth / w - 100;
} else {
w = PhdWinWidth;
h = (double)PhdWinWidth / pictureAspect;
x = 0;
y = (PhdWinHeight - h) / 2;
stretch = 100 * PhdWinHeight / h - 100;
}
if( stretch <= PictureStretchLimit ) {
*rect = PhdWinRect;
return 1;
}
rect->left = x;
rect->top = y;
rect->right = x + w;
rect->bottom = y + h;
return 0;
}
#endif // FEATURE_BACKGROUND_IMPROVED
================================================
FILE: modding/background_new.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef BACKGROUND_NEW_H_INCLUDED
#define BACKGROUND_NEW_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void PSX_Background(HWR_TEXHANDLE texSource, int tu, int tv, int t_width, int t_height, int halfRowCount,
__int16 amplitude, __int16 deformWavePhase, __int16 shortWavePhase, __int16 longWavePhase);
void BGND2_CleanupCaptureTextures();
int BGND2_PrepareCaptureTextures();
int __cdecl BGND2_FadeTo(int target, int delta);
int __cdecl BGND2_CapturePicture();
int __cdecl BGND2_LoadPicture(LPCTSTR fileName, BOOL isTitle, BOOL isReload);
int __cdecl BGND2_ShowPicture(DWORD fadeIn, DWORD waitIn, DWORD fadeOut, DWORD waitOut, BOOL inputCheck);
void __cdecl BGND2_DrawTextures(RECT *rect, D3DCOLOR color);
int __cdecl BGND2_CalculatePictureRect(RECT *rect);
#endif // BACKGROUND_NEW_H_INCLUDED
================================================
FILE: modding/cd_pauld.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* CD Audio solution in this file was designed by PaulD.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/sound.h"
#include "modding/cd_pauld.h"
#include "global/vars.h"
#define CD_ALIAS "T2"
typedef struct TrackInfo_t {
DWORD from;
DWORD to;
bool active;
} TRACK_INFO;
static TRACK_INFO Tracks[60];
static bool isCDAudioEnabled = false;
bool __cdecl PaulD_CD_Init() {
static LPCTSTR audioFiles[2] = {
"audio\\cdaudio.mp3",
"audio\\cdaudio.wav", // MCI volume control doesn't work with waveaudio. Wave option added just in case
};
static LPCTSTR audioTypes[2] = {
"mpegvideo",
"waveaudio",
};
HANDLE hFile;
DWORD fileSize, bytesRead, offset;
char cmdString[256];
char *buf;
int rc;
int audioType = -1;
if( isCDAudioEnabled )
return true;
for( int i=0; i<2; ++i ) {
hFile = CreateFile(audioFiles[i], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile != INVALID_HANDLE_VALUE ) {
audioType = i;
CloseHandle(hFile);
break;
}
}
if( audioType < 0 )
return false;
hFile = CreateFile("audio\\cdaudio.dat", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE )
return false;
fileSize = GetFileSize(hFile, NULL);
buf = (char *)malloc(fileSize);
if( buf == NULL ) {
CloseHandle(hFile);
return false;
}
rc = ReadFile(hFile, buf, fileSize, &bytesRead, NULL);
CloseHandle(hFile);
if( !rc || bytesRead == 0 ) return false;
memset(Tracks, 0, sizeof(Tracks));
offset = 0;
while( offset < bytesRead ) {
DWORD index, from, to;
while( buf[offset] == 0x0A || buf[offset] == 0x0D ) {
if( ++offset >= bytesRead ) goto PARSE_END; // skip empty lines
}
rc = sscanf(&buf[offset], "%lu %lu %lu", &index, &from, &to); // track comment is ignored
if( rc == 3 && index > 0 && index <= 60 ) {
Tracks[index-1].active = true;
Tracks[index-1].from = from;
Tracks[index-1].to = to;
}
while( buf[offset] != 0x0A && buf[offset] != 0x0D ) {
if( ++offset >= bytesRead ) goto PARSE_END; // skip until newline
}
}
PARSE_END :
free(buf);
// Fix cdaudio.dat if it's broken (like the one from the Steam version)
for( DWORD i=0; i= Tracks[i].to && i < ARRAY_SIZE(Tracks)-1 ) {
// Trying to fix data using the next track
for( DWORD j=i+1; j= Tracks[i].to && i > 0 ) {
// Trying to fix data using the previous track
for( int j=i-1; j>=0; --j ) {
if( Tracks[j].active ) {
Tracks[i].from = Tracks[j].to;
break;
}
}
}
}
wsprintf(cmdString, "open %s type %s alias " CD_ALIAS, audioFiles[audioType], audioTypes[audioType]);
rc = mciSendString(cmdString, NULL, 0, 0);
if( rc == 0 ) {
mciSendString("set " CD_ALIAS " time format ms", NULL, 0, 0);
isCDAudioEnabled = true;
}
return true;
}
void __cdecl PaulD_CD_Cleanup() {
if( isCDAudioEnabled ) {
PaulD_CDStop();
mciSendString("close " CD_ALIAS, NULL, 0, 0);
isCDAudioEnabled = false;
}
}
void __cdecl PaulD_CDLoop() {
int rc;
char statusString[32];
char cmdString[256];
if( CD_LoopTrack == 0 || ++CD_LoopCounter < 150 )
return;
CD_LoopCounter = 0;
rc = mciSendString("status " CD_ALIAS " mode", statusString, sizeof(statusString), 0);
if( (rc == 0) && !strncmp(statusString, "stopped", sizeof(statusString)) ) {
wsprintf(cmdString, "play " CD_ALIAS " from %lu to %lu", Tracks[CD_LoopTrack-1].from, Tracks[CD_LoopTrack-1].to);
mciSendString(cmdString, NULL, 0, 0);
}
}
void __cdecl PaulD_CDPlay(__int16 trackID, BOOL isLooped) {
__int16 track;
char cmdString[256];
if( MusicVolume == 0 )
return;
track = GetRealTrack(trackID);
if( track < 1 || !Tracks[track-1].active )
return;
CD_TrackID = trackID;
wsprintf(cmdString, "play " CD_ALIAS " from %lu to %lu", Tracks[track-1].from, Tracks[track-1].to);
mciSendString(cmdString, NULL, 0, 0);
if( isLooped ) {
CD_LoopTrack = track;
CD_LoopCounter = 120;
}
}
void __cdecl PaulD_CDStop() {
if( CD_TrackID > 0 ) {
mciSendString("stop " CD_ALIAS, NULL, 0, 0);
CD_TrackID = 0;
CD_LoopTrack = 0;
}
}
BOOL __cdecl PaulD_StartSyncedAudio(int trackID) {
__int16 track;
char cmdString[256];
track = GetRealTrack(trackID);
if( track < 1 || !Tracks[track-1].active )
return FALSE;
CD_TrackID = trackID;
if( 0 != mciSendString("set " CD_ALIAS " time format ms", NULL, 0, 0) )
return FALSE;
wsprintf(cmdString, "play " CD_ALIAS " from %lu to %lu", Tracks[track-1].from, Tracks[track-1].to);
return ( 0 == mciSendString(cmdString, NULL, 0, 0) );
}
DWORD __cdecl PaulD_CDGetLoc() {
__int16 track;
char statusString[32];
if( 0 != mciSendString("status " CD_ALIAS " position", statusString, sizeof(statusString), 0) )
return 0;
track = GetRealTrack(CD_TrackID);
// calculate audio frames position (75 audio frames per second)
return (atol(statusString) - Tracks[track-1].from) * 75 / 1000;
}
void __cdecl PaulD_CDVolume(DWORD volume) {
char cmdString[256];
if( volume > 0 )
volume = (volume - 5) * 4; // 0..255 -> 0..1000
wsprintf(cmdString, "setaudio " CD_ALIAS " volume to %lu", volume);
mciSendString(cmdString, NULL, 0, 0);
char statusString[32];
if( 0 == mciSendString("status " CD_ALIAS " mode", statusString, sizeof(statusString), 0) ) {
if( volume > 0 ) {
if( !strncmp(statusString, "paused", sizeof(statusString)) ) {
mciSendString("resume " CD_ALIAS, NULL, 0, 0);
}
} else {
if( !strncmp(statusString, "playing", sizeof(statusString)) ) {
mciSendString("pause " CD_ALIAS, NULL, 0, 0);
}
}
}
}
================================================
FILE: modding/cd_pauld.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* CD Audio solution in this file was designed by PaulD.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef CD_PAULD_H_INCLUDED
#define CD_PAULD_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
bool __cdecl PaulD_CD_Init();
void __cdecl PaulD_CD_Cleanup();
void __cdecl PaulD_CDLoop();
void __cdecl PaulD_CDPlay(__int16 trackID, BOOL isLooped);
void __cdecl PaulD_CDStop();
BOOL __cdecl PaulD_StartSyncedAudio(int trackID);
DWORD __cdecl PaulD_CDGetLoc();
void __cdecl PaulD_CDVolume(DWORD volume);
#endif // CD_PAULD_H_INCLUDED
================================================
FILE: modding/file_utils.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "modding/file_utils.h"
#include "global/vars.h"
int PathStringCombine(LPSTR destPath, DWORD destSize, LPCSTR filePath, LPCSTR fileName) {
if( destPath == NULL || destSize == 0 || fileName == NULL || *fileName == 0 ) {
return -1;
}
char *tmpPath = new char[destSize];
if( filePath == NULL || *filePath == 0 ) {
strncpy(tmpPath, ".", destSize);
} else {
strncpy(tmpPath, filePath, destSize);
PathRemoveBackslash(tmpPath);
}
snprintf(destPath, destSize, "%s\\%s", tmpPath, fileName);
delete[] tmpPath;
return 0;
}
int AutoSelectExtension(LPSTR fileName, const STRING_FIXED4 *exts, DWORD num_exts) {
if( fileName == NULL ) {
return -1;
}
if( exts != NULL && num_exts > 0 ) {
char extFileName[256] = {0};
char *extension;
strncpy(extFileName, fileName, sizeof(extFileName)-1);
extension = PathFindExtension(extFileName);
if( extension == NULL ) {
extension = strchr(extFileName, 0);
*extension = '.';
}
for( DWORD i = 0; i < num_exts; ++i ) {
memcpy(extension + 1, &exts[i], sizeof(STRING_FIXED4));
if( PathFileExists(extFileName) ) {
strcpy(fileName, extFileName);
return (i + 1);
}
}
}
return ( PathFileExists(fileName) ) ? 0 : -1;
}
int AutoSelectPathAndExtension(LPSTR fileName, LPCSTR path, const STRING_FIXED4 *exts, DWORD num_exts) {
char checkFileName[MAX_PATH] = {0};
int ext;
if( fileName == NULL || !*fileName ) {
return -1;
}
if( path != NULL && *path ) {
LPCSTR fname = PathFindFileName(fileName);
PathStringCombine(checkFileName, sizeof(checkFileName), path, fname);
ext = AutoSelectExtension(checkFileName, exts, num_exts);
if( ext >= 0 ) {
strcpy(fileName, checkFileName);
return 1;
}
}
ext = AutoSelectExtension(fileName, exts, num_exts);
return ( ext >= 0 ) ? 0 : -1;
}
int CreateDirectories(LPCSTR path, bool isFileName) {
char shortPath[MAX_PATH];
char fullPath[MAX_PATH];
strncpy(shortPath, path, sizeof(shortPath)-1);
if( isFileName ) {
PathRemoveFileSpec(shortPath);
}
GetFullPathName(shortPath, MAX_PATH, fullPath, NULL);
switch( SHCreateDirectoryExA(NULL, fullPath, NULL) ) {
case ERROR_ALREADY_EXISTS :
case ERROR_FILE_EXISTS :
case ERROR_SUCCESS :
return 0;
default :
break;
}
return -1;
}
int CreateSequenceFilename(LPSTR destName, DWORD destSize, LPCSTR filePath, LPCSTR fileExt, LPCSTR nameBase, int seqDigits, int seqNumber) {
char shortName[32] = {0};
int i;
if( destName == NULL || seqNumber < 0 || seqDigits <= 0 ) {
return -1;
}
if( filePath == NULL ) filePath = "";
if( nameBase == NULL ) nameBase = "";
if( fileExt == NULL ) fileExt = "";
int seqMax = 1;
for( i = 0; i < seqDigits; ++i ) {
seqMax *= 10;
}
for( i = seqNumber; i < seqMax; ++i ) { // search first free screenshot slot
snprintf(shortName, sizeof(shortName), "%s%0*d%s", nameBase, seqDigits, i, fileExt);
PathStringCombine(destName, destSize, filePath, shortName);
if( INVALID_FILE_ATTRIBUTES == GetFileAttributes(destName) ) {
return i; // no file with such name - we have found the free slot
}
}
return -2;
}
int CreateDateTimeFilename(LPSTR destName, DWORD destSize, LPCSTR filePath, LPCSTR fileExt, SYSTEMTIME *lastTime, int *lastIndex) {
char shortName[32] = {0};
char fileName[32] = {0};
SYSTEMTIME sysTime;
if( destName == NULL ) {
return -1;
}
if( filePath == NULL ) filePath = "";
if( fileExt == NULL ) fileExt = "";
GetLocalTime(&sysTime);
sysTime.wMilliseconds = 0; // we don't use milliseconds
snprintf(shortName, sizeof(shortName), "%04d%02d%02d_%02d%02d%02d",
sysTime.wYear, sysTime.wMonth, sysTime.wDay,
sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
if( lastTime != NULL && lastIndex != NULL && !memcmp(&sysTime, lastTime, sizeof(SYSTEMTIME)) ) {
snprintf(fileName, sizeof(fileName), "%s_%d%s", shortName, ++*lastIndex, fileExt);
} else {
if( lastTime != NULL && lastIndex != NULL ) {
*lastIndex = 0;
*lastTime = sysTime;
}
snprintf(fileName, sizeof(fileName), "%s%s", shortName, fileExt);
}
PathStringCombine(destName, destSize, filePath, fileName);
return 0;
}
int AddFilenameSuffix(LPSTR destName, DWORD destSize, LPCSTR fileName, LPCSTR suffix) {
if( destName == NULL || !destSize || fileName == NULL || suffix == NULL ) {
return -1;
}
LPCSTR extension = PathFindExtension(fileName);
int nameLen = (DWORD)extension - (DWORD)fileName;
snprintf(destName, destSize, "%.*s%s%s", nameLen, fileName, suffix, extension);
return 0;
}
LPCVOID GetResourceData(LPCTSTR resName, LPDWORD resSize) {
extern HINSTANCE hInstance;
HRSRC resInfo = FindResource(hInstance, resName, RT_RCDATA);
if( !resInfo ) return NULL;
if( resSize ) *resSize = SizeofResource(hInstance, resInfo);
HGLOBAL resHandle = LoadResource(hInstance, resInfo);
if( !resHandle ) return NULL;
return LockResource(resHandle);
}
================================================
FILE: modding/file_utils.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef FILE_UTILS_H_INCLUDED
#define FILE_UTILS_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
int PathStringCombine(LPSTR destPath, DWORD destSize, LPCSTR filePath, LPCSTR fileName);
int AutoSelectExtension(LPSTR fileName, const STRING_FIXED4 *exts, DWORD num_exts);
int AutoSelectPathAndExtension(LPSTR fileName, LPCSTR path, const STRING_FIXED4 *exts, DWORD num_exts);
int CreateDirectories(LPCSTR path, bool isFileName);
int CreateSequenceFilename(LPSTR destName, DWORD destSize, LPCSTR filePath, LPCSTR fileExt, LPCSTR nameBase, int seqDigits, int seqNumber);
int CreateDateTimeFilename(LPSTR fileName, DWORD destSize, LPCSTR filePath, LPCSTR fileExt, SYSTEMTIME *lastTime, int *lastIndex);
int AddFilenameSuffix(LPSTR destName, DWORD destSize, LPCSTR fileName, LPCSTR suffix);
LPCVOID GetResourceData(LPCTSTR resName, LPDWORD resSize);
#endif // FILE_UTILS_H_INCLUDED
================================================
FILE: modding/gdi_utils.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "modding/gdi_utils.h"
#include "global/vars.h"
#include
using namespace Gdiplus;
static const WCHAR *GDI_Encoders[] = {
L"image/bmp",
L"image/jpeg",
L"image/png",
};
static ULONG_PTR GDI_Token = 0;
static int GetEncoderClsid(const WCHAR *format, CLSID *pClsid) {
unsigned int num = 0, nSize = 0;
GetImageEncodersSize(&num, &nSize);
if( nSize == 0 ) {
return -1;
}
ImageCodecInfo *pImageCodecInfo = (ImageCodecInfo *)malloc(nSize);
if( pImageCodecInfo == NULL ) {
return -1;
}
GetImageEncoders(num, nSize, pImageCodecInfo);
for( DWORD j = 0; j < num; ++j) {
if(wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}
}
free(pImageCodecInfo);
return -1;
}
HBITMAP CreateBitmapFromDC(HDC dc, RECT *rect, LPVOID *lpBits, PALETTEENTRY *pal) {
if( dc == NULL || rect == NULL || lpBits == NULL ) {
return NULL; // wrong parameters
}
WORD nBPP = GetDeviceCaps(dc, BITSPIXEL);
int width = ABS(rect->right - rect->left);
int height = ABS(rect->bottom - rect->top);
HDC hdcDestination = CreateCompatibleDC(dc);
DWORD infoSize = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
BITMAPINFO *info = (BITMAPINFO *)malloc(infoSize);
if( info == NULL ) {
DeleteDC(hdcDestination);
return NULL; // failed to create bitmap info
}
memset(info, 0, infoSize);
info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info->bmiHeader.biWidth = width;
info->bmiHeader.biHeight = -height;
info->bmiHeader.biPlanes = 1;
info->bmiHeader.biBitCount = nBPP;
info->bmiHeader.biCompression = BI_RGB;
if( pal != NULL ) {
for( int i = 0; i < 256; ++i ) {
info->bmiColors[i].rgbRed = pal[i].peRed;
info->bmiColors[i].rgbGreen = pal[i].peGreen;
info->bmiColors[i].rgbBlue = pal[i].peBlue;
}
}
HBITMAP hbmDestination = CreateDIBSection(dc, info, DIB_RGB_COLORS, lpBits, NULL, 0);
free(info);
if( hbmDestination == NULL ) {
DeleteDC(hdcDestination);
return NULL; // failed to create bitmap
}
int stateDestination = SaveDC(hdcDestination);
SelectObject(hdcDestination, hbmDestination);
BitBlt(hdcDestination, 0, 0, width, height, dc, rect->left, rect->top, SRCCOPY);
RestoreDC(hdcDestination, stateDestination);
DeleteDC(hdcDestination);
return hbmDestination;
}
bool __cdecl GDI_Init() {
if( !GDI_Token ) {
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&GDI_Token, &gdiplusStartupInput, NULL);
}
return ( GDI_Token != 0 );
}
void __cdecl GDI_Cleanup() {
if( GDI_Token ) {
GdiplusShutdown(GDI_Token);
GDI_Token = 0;
}
}
int GDI_SaveImageFile(LPCSTR filename, GDI_FILEFMT format, DWORD quality, HBITMAP hbmBitmap) {
if( filename == NULL || !*filename || hbmBitmap == NULL ) {
return -1; // wrong parameters
}
if( format < 0 || format >= ARRAY_SIZE(GDI_Encoders) ) {
return -1; // wrong format
}
Status status = Ok;
CLSID imageCLSID;
Bitmap *gdi_bitmap = new Bitmap(hbmBitmap, (HPALETTE)NULL);
if( gdi_bitmap == NULL ) {
return -1;
}
EncoderParameters encoderParams;
encoderParams.Count = 1;
encoderParams.Parameter[0].NumberOfValues = 1;
encoderParams.Parameter[0].Guid = EncoderQuality;
encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParams.Parameter[0].Value = &quality;
GetEncoderClsid(GDI_Encoders[format], &imageCLSID);
#ifdef UNICODE
status = gdi_bitmap->Save(filename, &imageCLSID, &encoderParams);
#else // !UNICODE
{
WCHAR wc_fname[MAX_PATH] = {0};
if( !MultiByteToWideChar(CP_ACP, 0, filename, strlen(filename), wc_fname, MAX_PATH) ) {
delete gdi_bitmap;
return -1;
}
status = gdi_bitmap->Save(wc_fname, &imageCLSID, &encoderParams);
}
#endif // UNICODE
delete gdi_bitmap;
return ( status == Ok ) ? 0 : -1;
}
int GDI_LoadImageFile(LPCSTR filename, BYTE **bmPtr, DWORD *width, DWORD *height, DWORD bpp) {
if( filename == NULL || !*filename || bmPtr == NULL || width == NULL || height == NULL ) {
return -1; // wrong parameters
}
DWORD i;
int result = 0;
DWORD bmSize = 0;
BYTE *src = NULL;
BYTE *dst = NULL;
Bitmap *gdi_bitmap = NULL;
BitmapData bmData;
Status status;
PixelFormat pixelFmt;
switch( bpp ) {
case 32 :
pixelFmt = PixelFormat32bppARGB;
break;
case 16 :
pixelFmt = PixelFormat16bppARGB1555;
break;
default :
// unsupported pixel format;
return -1;
break;
}
#ifdef UNICODE
gdi_bitmap = new Bitmap(filename);
#else // !UNICODE
{
WCHAR wc_fname[MAX_PATH] = {0};
if( !MultiByteToWideChar(CP_ACP, 0, filename, strlen(filename), wc_fname, MAX_PATH) ) {
// failed to get UNICODE filename
result = -1;
goto CLEANUP;
}
gdi_bitmap = new Bitmap(wc_fname);
}
#endif // UNICODE
if( gdi_bitmap == NULL ) {
// failed to create gdi_bitmap
result = -1;
goto CLEANUP;
}
*width = gdi_bitmap->GetWidth();
*height = gdi_bitmap->GetHeight();
bmSize = (*width) * (*height) * (bpp/8);
*bmPtr = (BYTE *)malloc(bmSize * (bpp/8));
if( *bmPtr == NULL ) {
// failed to allocate output bitmap
result = -1;
goto CLEANUP;
}
{ // rect is temporary here
Rect rect(0, 0, *width, *height);
status = gdi_bitmap->LockBits(&rect, ImageLockModeRead, pixelFmt, &bmData);
}
if( status != Ok ) {
// failed to lock the bitmap
free(bmPtr);
result = -1;
goto CLEANUP;
}
src = (BYTE *)bmData.Scan0;
if( bmData.Stride < 0 ) {
src += ABS(bmData.Stride) * (*height - 1);
}
dst = *bmPtr;
for( i = 0; i < *height; ++i ) {
memcpy(dst, src, (*width) * (bpp/8));
dst += (*width) * (bpp/8);
src += bmData.Stride;
}
gdi_bitmap->UnlockBits(&bmData);
CLEANUP :
if( gdi_bitmap != NULL ) {
delete gdi_bitmap;
gdi_bitmap = NULL;
}
return result;
}
================================================
FILE: modding/gdi_utils.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef GDI_UTILS_H_INCLUDED
#define GDI_UTILS_H_INCLUDED
#include "global/types.h"
typedef enum {
GDI_BMP,
GDI_JPG,
GDI_PNG,
} GDI_FILEFMT;
/*
* Function list
*/
HBITMAP CreateBitmapFromDC(HDC dc, RECT *rect, LPVOID *lpBits, PALETTEENTRY *pal);
bool __cdecl GDI_Init();
void __cdecl GDI_Cleanup();
int GDI_SaveImageFile(LPCSTR filename, GDI_FILEFMT format, DWORD quality, HBITMAP hbmBitmap);
int GDI_LoadImageBitmap(HBITMAP hbmBitmap, BYTE **bmPtr, DWORD *width, DWORD *height, DWORD bpp);
int GDI_LoadImageFile(LPCSTR filename, BYTE **bmPtr, DWORD *width, DWORD *height, DWORD bpp);
#endif // GDI_UTILS_H_INCLUDED
================================================
FILE: modding/joy_output.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "modding/joy_output.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
#define NUM_MOTORS (2)
#define NUM_VIBS (16)
#define HP_100 (1000)
#define HP_50 (HP_100/2)
#define AIR_100 (1800)
#define AIR_75 (AIR_100*3/4)
#define AIR_50 (AIR_100/2)
#define AIR_25 (AIR_100/4)
extern void SetJoystickOutput(WORD leftMotor, WORD rightMotor, DWORD ledColor);
extern bool IsJoyVibrationEnabled();
extern bool IsJoyLedColorEnabled();
typedef struct {
int inc, val, sus, dec, len, stage, value;
} VIBRATION;
static VIBRATION Vib[NUM_MOTORS][NUM_VIBS];
static DWORD LedColor = 0;
static int BlendLedColor(int c1, int c2) {
if( c1 < c2 ) {
c1 += 16;
CLAMPG(c1 ,c2);
} else if( c1 > c2 ) {
c1 -= 16;
CLAMPL(c1 ,c2);
}
return c1;
}
static void SetupVibration(int motor, int inc, int val, int sus, int dec, int len, bool isCamera) {
int id = 0;
if( !isCamera ) {
int id = 1;
for( int i=1; ipos.x : Camera.pos.x;
y -= fromLara ? LaraItem->pos.y : Camera.pos.y;
z -= fromLara ? LaraItem->pos.z : Camera.pos.z;
DWORD dist = SQR(x) + SQR(y) + SQR(z);
if( dist < SQR(range) ) {
// it is intended to be non-linear
dist = 0xFFFF - (UINT64)dist*0xFFFF/SQR(range);
JoyRumble(dist/8, dist, 20, dist/32, 40, false);
}
}
void UpdateJoyOutput(bool isInGame) {
WORD motor[2] = {0, 0};
for( int i=0; i Vib[i][j].sus) {
break;
}
Vib[i][j].stage = 2;
// fall through
case 2:
Vib[i][j].value -= Vib[i][j].dec;
if( Vib[i][j].value > 0 ) {
break;
}
Vib[i][j].value = 0;
Vib[i][j].stage = 3;
// fall through
default:
break;
}
CLAMP(Vib[i][j].value, 0, 0xFFFF);
CLAMPL(motor[i], Vib[i][j].value);
--Vib[i][j].len;
}
}
int r=0, g=0, b=0;
bool isInjured = false;
if( !IsJoyLedColorEnabled() || !isInGame || LaraItem == NULL ) {
r = RGB_GETRED (DEFAULT_JOYSTICK_LED_COLOR);
g = RGB_GETGREEN(DEFAULT_JOYSTICK_LED_COLOR);
b = RGB_GETBLUE (DEFAULT_JOYSTICK_LED_COLOR);
} else {
static __int16 hitPoints = 0;
isInjured = (LaraItem->hitPoints < hitPoints && Lara.air > 0);
hitPoints = LaraItem->hitPoints;
if( isInjured ) {
r = 255;
if( hitPoints > HP_50 ) {
g = 255 * (hitPoints - HP_50) / HP_50;
}
} else {
if( Lara.water_status == LWS_Underwater ) {
if( hitPoints > 0 && Lara.air > 0 ) {
if( Lara.air > AIR_75 ) {
g = 255;
b = 128 + 127 * (AIR_100 - Lara.air) / AIR_25;
} else if( Lara.air > AIR_25 ) {
g = 255 * (Lara.air - AIR_25) / AIR_50;
b = 255;
} else {
r = 127 * (AIR_25 - Lara.air) / AIR_25;
b = 255;
}
} else {
r = 128 + 127 * (HP_100 - hitPoints) / HP_100;
b = 255 * hitPoints / HP_100;
}
} else {
if( hitPoints > HP_50 ) {
r = 255 * (HP_100 - hitPoints) / HP_50;
g = 255;
} else {
r = 255;
g = 255 * hitPoints / HP_50;
}
}
if( hitPoints > 0 && Lara.air > 0 && Lara.gun_status != LGS_Ready ) {
r /= 3;
g /= 3;
b /= 3;
} else {
r = r*2/3;
g = g*2/3;
b = b*2/3;
}
}
}
if( isInjured ) {
LedColor = RGB_MAKE(r, g, b);
} else if( LedColor != RGB_MAKE(r, g, b) ) {
r = BlendLedColor(RGB_GETRED(LedColor), r);
g = BlendLedColor(RGB_GETGREEN(LedColor), g);
b = BlendLedColor(RGB_GETBLUE(LedColor), b);
LedColor = RGB_MAKE(r, g, b);
}
SetJoystickOutput(motor[0], motor[1], LedColor);
}
#endif // FEATURE_INPUT_IMPROVED
================================================
FILE: modding/joy_output.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef JOY_OUTPUT_H_INCLUDED
#define JOY_OUTPUT_H_INCLUDED
#include "global/types.h"
#define DEFAULT_JOYSTICK_LED_COLOR (0x303030)
/*
* Function list
*/
void JoyOutputReset();
void JoyVibrationMute();
void JoyVibrate(int acc, int lev, int sus, int dec, int len, bool isCamera);
void JoyRumble(int acc, int lev, int sus, int dec, int len, bool isCamera);
void JoyRumbleExplode(int x, int y, int z, DWORD range, bool fromLara);
void UpdateJoyOutput(bool isInGame);
#endif // JOY_OUTPUT_H_INCLUDED
================================================
FILE: modding/json_utils.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "modding/json_utils.h"
#include "global/vars.h"
json_value *GetJsonField(json_value *root, json_type fieldType, const char *name, DWORD *pIndex) {
if( root == NULL || root->type != json_object ) {
return NULL;
}
json_value *result = NULL;
DWORD len = name ? strlen(name) : 0;
DWORD i = pIndex ? *pIndex : 0;
for( ; i < root->u.object.length; ++i ) {
if( root->u.object.values[i].value->type == fieldType ) {
if( !name || (len == root->u.object.values[i].name_length
&& !strncmp(root->u.object.values[i].name, name, len)) )
{
result = root->u.object.values[i].value;
break;
}
}
}
if( pIndex ) *pIndex = i;
return result;
}
json_value *GetJsonObjectByStringField(json_value *root, const char *name, const char *str, bool caseSensitive, DWORD *pIndex) {
if( root == NULL || root->type != json_array || !name || !*name || !str ) {
return NULL;
}
json_value *result = NULL;
DWORD len = strlen(str);
DWORD i = pIndex ? *pIndex : 0;
for( ; i < root->u.array.length; ++i ) {
json_value *key = GetJsonField(root->u.array.values[i], json_string, name, NULL);
if( key && len == key->u.string.length &&
(caseSensitive ? strncmp(key->u.string.ptr, str, len) : !strncasecmp(key->u.string.ptr, str, len)) )
{
result = root->u.array.values[i];
break;
}
}
if( pIndex ) *pIndex = i;
return result;
}
int GetJsonIntegerFieldValue(json_value *root, const char *name, int defaultValue) {
json_value *field = GetJsonField(root, json_integer, name, NULL);
return field ? field->u.integer : defaultValue;
}
double GetJsonFloatFieldValue(json_value *root, const char *name, double defaultValue) {
json_value *field = GetJsonField(root, json_double, name, NULL);
return field ? field->u.dbl : defaultValue;
}
================================================
FILE: modding/json_utils.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef JSON_UTILS_H_INCLUDED
#define JSON_UTILS_H_INCLUDED
#include "global/types.h"
#include "json-parser/json.h"
json_value *GetJsonField(json_value *root, json_type fieldType, const char *name, DWORD *pIndex);
json_value *GetJsonObjectByStringField(json_value *root, const char *name, const char *str, bool caseSensitive, DWORD *pIndex);
int GetJsonIntegerFieldValue(json_value *root, const char *name, int defaultValue);
double GetJsonFloatFieldValue(json_value *root, const char *name, double defaultValue);
#endif // JSON_UTILS_H_INCLUDED
================================================
FILE: modding/mod_utils.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "modding/mod_utils.h"
#include "modding/json_utils.h"
#include "global/vars.h"
#ifdef FEATURE_MOD_CONFIG
#define MOD_CONFIG_NAME "TR2Main.json"
typedef struct {
bool isLoaded;
POLYINDEX *animtex;
POLYFILTER_NODE *rooms;
POLYFILTER_NODE *statics;
POLYFILTER_NODE *objects[ID_NUMBER_OBJECTS];
} SEMITRANS_CONFIG;
typedef struct {
bool isLoaded;
POLYFILTER_NODE *statics;
POLYFILTER_NODE *objects[ID_NUMBER_OBJECTS];
} REFLECT_CONFIG;
typedef struct {
bool isLoaded;
bool isBarefoot;
char loadingPix[256];
DWORD waterColor;
SEMITRANS_CONFIG semitrans;
REFLECT_CONFIG reflect;
} MOD_CONFIG;
static MOD_CONFIG ModConfig;
static POLYFILTER *CreatePolyfilterNode(POLYFILTER_NODE **root, int id) {
if( root == NULL ) return NULL;
POLYFILTER_NODE *node = (POLYFILTER_NODE *)malloc(sizeof(POLYFILTER_NODE));
if( node == NULL ) return NULL;
node->id = id;
node->next = *root;
memset(&node->filter, 0, sizeof(node->filter));
*root = node;
return &node->filter;
}
static void FreePolyfilterNodes(POLYFILTER_NODE **root) {
if( root == NULL ) return;
POLYFILTER_NODE *node = *root;
while( node ) {
POLYFILTER_NODE *next = node->next;
free(node);
node = next;
}
*root = NULL;
}
#endif // FEATURE_MOD_CONFIG
static bool IsCompatibleFilter(__int16 *ptrObj, bool isRoomMesh, POLYFILTER *filter) {
if( !ptrObj || !filter || !filter->n_vtx ) return true;
if( !isRoomMesh ) {
ptrObj += 5; // skip x, y, z, radius, flags
}
__int16 num = *(ptrObj++); // get vertex counter
if( num != filter->n_vtx ) return false;
ptrObj += num * (isRoomMesh ? 6 : 3); // skip vertices
if( !isRoomMesh ) {
num = *(ptrObj++); // get normal counter
ptrObj += (num > 0) ? num * 3 : ABS(num); // skip normals/shades
}
num = *(ptrObj++); // get gt4 number
if( num != filter->n_gt4 ) return false;
ptrObj += num * 5; // skip gt4 polys
num = *(ptrObj++); // get gt3 number
if( num != filter->n_gt3 ) return false;
if( !isRoomMesh ) {
ptrObj += num * 4; // skip gt3 polys
num = *(ptrObj++); // get g4 number
if( num != filter->n_g4 ) return false;
ptrObj += num * 5; // skip g4 polys
num = *(ptrObj++); // get g3 number
if( num != filter->n_g3 ) return false;
}
return true;
}
static __int16 *EnumeratePolysSpecific(__int16 *ptrObj, int vtxCount, bool colored, ENUM_POLYS_CB callback, POLYINDEX *filter, LPVOID param) {
int polyNumber = *ptrObj++;
if( filter == NULL || (!filter[0].idx && !filter[0].num) ) {
for( int i = 0; i < polyNumber; ++i ) {
if( !callback(ptrObj, vtxCount, colored, param) ) return NULL;
ptrObj += vtxCount + 1;
}
} else {
int polyIndex = 0;
for( int i=0; i= polyNumber ) {
break;
}
int skip = filter[i].idx - polyIndex;
if( skip > 0 ) {
ptrObj += skip*(vtxCount+1);
polyIndex += skip;
}
int number = MIN(filter[i].num, polyNumber - polyIndex);
for( int j = 0; j < number; ++j ) {
if( !callback(ptrObj, vtxCount, colored, param) ) return NULL;
ptrObj += vtxCount + 1;
}
polyIndex += number;
}
ptrObj += (polyNumber-polyIndex)*(vtxCount+1);
}
return ptrObj;
}
bool EnumeratePolys(__int16 *ptrObj, bool isRoomMesh, ENUM_POLYS_CB callback, POLYFILTER *filter, LPVOID param) {
if( ptrObj == NULL || callback == NULL ) return false; // wrong parameters
if( !IsCompatibleFilter(ptrObj, isRoomMesh, filter) ) return false; // filter is not compatible
__int16 num;
if( !isRoomMesh ) {
ptrObj += 5; // skip x, y, z, radius, flags
}
num = *(ptrObj++); // get vertex counter
ptrObj += num * (isRoomMesh ? 6 : 3); // skip vertices
if( !isRoomMesh ) {
num = *(ptrObj++); // get normal counter
ptrObj += (num > 0) ? num * 3 : ABS(num); // skip normals/shades
}
ptrObj = EnumeratePolysSpecific(ptrObj, 4, false, callback, filter ? filter->gt4 : NULL, param); // enumerate textured quads
if( ptrObj == NULL ) return true;
ptrObj = EnumeratePolysSpecific(ptrObj, 3, false, callback, filter ? filter->gt3 : NULL, param); // enumerate textured triangles
if( !isRoomMesh ) {
if( ptrObj == NULL ) return true;
ptrObj = EnumeratePolysSpecific(ptrObj, 4, true, callback, filter ? filter->g4 : NULL, param); // enumerate colored quads
if( ptrObj == NULL ) return true;
ptrObj = EnumeratePolysSpecific(ptrObj, 3, true, callback, filter ? filter->g3 : NULL, param); // enumerate colored triangles
}
return true;
}
#ifdef FEATURE_MOD_CONFIG
bool IsModConfigLoaded() {
return ModConfig.isLoaded;
}
bool IsModBarefoot() {
return ModConfig.isBarefoot;
}
const char *GetModLoadingPix() {
return *ModConfig.loadingPix ? ModConfig.loadingPix : NULL;
}
DWORD GetModWaterColor() {
return ModConfig.waterColor;
}
bool IsModSemitransConfigLoaded() {
return ModConfig.semitrans.isLoaded;
}
POLYINDEX *GetModSemitransAnimtexFilter() {
return ModConfig.semitrans.animtex;
}
POLYFILTER_NODE *GetModSemitransRoomsFilter() {
return ModConfig.semitrans.rooms;
}
POLYFILTER_NODE *GetModSemitransStaticsFilter() {
return ModConfig.semitrans.statics;
}
POLYFILTER_NODE **GetModSemitransObjectsFilter() {
return ModConfig.semitrans.objects;
}
bool IsModReflectConfigLoaded() {
return ModConfig.reflect.isLoaded;
}
POLYFILTER_NODE *GetModReflectStaticsFilter() {
return ModConfig.reflect.statics;
}
POLYFILTER_NODE **GetModReflectObjectsFilter() {
return ModConfig.reflect.objects;
}
static int ParsePolyString(const char *str, POLYINDEX *lst, DWORD lstLen){
if( !lst || !lstLen ) {
return -1;
}
lst[0].idx = ~0;
lst[0].num = ~0;
POLYINDEX *lstBuf = (POLYINDEX *)malloc(lstLen * sizeof(POLYINDEX));
if( lstBuf == NULL ) {
return -2;
}
char *strBuf = strdup(str);
if( strBuf == NULL ) {
free(lstBuf);
return -2;
}
DWORD bufLen = 0;
char *token = strtok(strBuf, ",");
while( token != NULL ) {
char *range = strchr(token, '-');
if( range ) {
int from = atoi(token);
int to = atoi(range + 1);
lstBuf[bufLen].idx = MIN(to, from);
lstBuf[bufLen].num = ABS(to - from) + 1;
} else {
lstBuf[bufLen].idx = atoi(token);
lstBuf[bufLen].num = 1;
}
if( ++bufLen >= lstLen ) {
break;
}
token = strtok(NULL, ",");
}
free(strBuf);
if( !bufLen ) {
free(lstBuf);
return 0;
}
for( DWORD i = 0; i < bufLen-1; ++i ) {
for( DWORD j = i+1; j < bufLen; ++j ) {
if( lstBuf[i].idx > lstBuf[j].idx ) {
POLYINDEX t;
SWAP(lstBuf[i], lstBuf[j], t);
}
}
}
lst[0] = lstBuf[0];
DWORD resLen = 1;
for( DWORD i = 1; i < bufLen; ++i ) {
int bound = lst[resLen-1].idx + lst[resLen-1].num;
if( lstBuf[i].idx > bound ) {
lst[resLen] = lstBuf[i];
++resLen;
} else {
int ext = lstBuf[i].idx + lstBuf[i].num;
if( ext > bound ) {
lst[resLen-1].num += ext - bound;
}
}
}
if( resLen < lstLen ) {
lst[resLen].idx = 0;
lst[resLen].num = 0;
}
free(lstBuf);
return resLen;
}
static int ParsePolyValue(json_value *value, POLYINDEX *lst, DWORD lstLen) {
if( !lst || !lstLen ) {
return -1;
}
lst[0].idx = ~0;
lst[0].num = ~0;
if( value == NULL ) {
return 0;
}
const char *str = value->u.string.ptr;
if( !str || !*str || !strcasecmp(str, "none") ) {
return 0;
}
if( !strcasecmp(str, "all") ) {
lst[0].idx = 0;
lst[0].num = 0;
return 1;
}
return ParsePolyString(str, lst, lstLen);
}
static bool ParsePolyfilterConfiguration(json_value *root, const char *name, POLYFILTER_NODE **pNodes) {
FreePolyfilterNodes(pNodes);
if( root == NULL || root->type != json_array || !name || !*name ) {
return false;
}
for( DWORD i = 0; i < root->u.array.length; ++i ) {
json_value *item = root->u.array.values[i];
json_value *field = GetJsonField(item, json_integer, name, NULL);
if( !field || field->u.integer < 0 ) continue;
POLYFILTER *filter = CreatePolyfilterNode(pNodes, field->u.integer);
if( !filter ) continue;
field = GetJsonField(item, json_object, "filter", NULL);
if( field ) {
filter->n_vtx = GetJsonIntegerFieldValue(field, "v", 0);
filter->n_gt4 = GetJsonIntegerFieldValue(field, "t4", 0);
filter->n_gt3 = GetJsonIntegerFieldValue(field, "t3", 0);
filter->n_g4 = GetJsonIntegerFieldValue(field, "c4", 0);
filter->n_g3 = GetJsonIntegerFieldValue(field, "c3", 0);
}
json_value *t4list = GetJsonField(item, json_string, "t4list", NULL);
json_value *t3list = GetJsonField(item, json_string, "t3list", NULL);
json_value *c4list = GetJsonField(item, json_string, "c4list", NULL);
json_value *c3list = GetJsonField(item, json_string, "c3list", NULL);
// If no lists presented, consider that lists set to "all"
if( t4list || t3list || c4list || c3list ) {
ParsePolyValue(t4list, filter->gt4, ARRAY_SIZE(filter->gt4));
ParsePolyValue(t3list, filter->gt3, ARRAY_SIZE(filter->gt3));
ParsePolyValue(c4list, filter->g4, ARRAY_SIZE(filter->g4));
ParsePolyValue(c3list, filter->g3, ARRAY_SIZE(filter->g3));
}
}
return true;
}
static bool ParseSemitransConfiguration(json_value *root) {
if( root == NULL || root->type != json_object ) {
return false;
}
json_value* field = NULL;
field = GetJsonField(root, json_string, "animtex", NULL);
if( field ) {
if( ModConfig.semitrans.animtex ) {
free(ModConfig.semitrans.animtex);
ModConfig.semitrans.animtex = NULL;
}
if( strcasecmp(field->u.string.ptr, "auto") ) {
ModConfig.semitrans.animtex = (POLYINDEX *)malloc(sizeof(POLYINDEX) * POLYFILTER_SIZE);
if( ModConfig.semitrans.animtex ) {
ParsePolyValue(field, ModConfig.semitrans.animtex, POLYFILTER_SIZE);
}
}
}
json_value* objects = GetJsonField(root, json_array, "objects", NULL);
if( objects ) {
for( DWORD i = 0; i < objects->u.array.length; ++i ) {
json_value *object = objects->u.array.values[i];
field = GetJsonField(object, json_integer, "object", NULL);
if( !field || field->u.integer < 0 || field->u.integer >= ARRAY_SIZE(ModConfig.semitrans.objects) ) continue;
ParsePolyfilterConfiguration(GetJsonField(object, json_array, "meshes", NULL), "mesh", &ModConfig.semitrans.objects[field->u.integer]);
}
}
ParsePolyfilterConfiguration(GetJsonField(root, json_array, "statics", NULL), "static", &ModConfig.semitrans.statics);
ParsePolyfilterConfiguration(GetJsonField(root, json_array, "rooms", NULL), "room", &ModConfig.semitrans.rooms);
ModConfig.semitrans.isLoaded = true;
return true;
}
static bool ParseReflectConfiguration(json_value *root) {
if( root == NULL || root->type != json_object ) {
return false;
}
json_value* field = NULL;
json_value* objects = GetJsonField(root, json_array, "objects", NULL);
if( objects ) {
for( DWORD i = 0; i < objects->u.array.length; ++i ) {
json_value *object = objects->u.array.values[i];
field = GetJsonField(object, json_integer, "object", NULL);
if( !field || field->u.integer < 0 || field->u.integer >= ARRAY_SIZE(ModConfig.reflect.objects) ) continue;
ParsePolyfilterConfiguration(GetJsonField(object, json_array, "meshes", NULL), "mesh", &ModConfig.reflect.objects[field->u.integer]);
}
}
ParsePolyfilterConfiguration(GetJsonField(root, json_array, "statics", NULL), "static", &ModConfig.reflect.statics);
ModConfig.reflect.isLoaded = true;
return true;
}
static bool ParseLevelConfiguration(json_value *root) {
if( root == NULL || root->type != json_object ) {
return false;
}
json_value* field = NULL;
field = GetJsonField(root, json_string, "picture", NULL);
if( field ) {
snprintf(ModConfig.loadingPix, sizeof(ModConfig.loadingPix), "data\\%.*s.pcx", field->u.string.length, field->u.string.ptr);
}
field = GetJsonField(root, json_string, "watercolor", NULL);
if( field && field->u.string.length == 6 ) {
ModConfig.waterColor = strtol(field->u.string.ptr, NULL, 16);
}
field = GetJsonField(root, json_boolean, "barefoot", NULL);
if( field ) {
ModConfig.isBarefoot = field->u.boolean;
}
ParseSemitransConfiguration(GetJsonField(root, json_object, "semitransparent", NULL));
ParseReflectConfiguration(GetJsonField(root, json_object, "reflective", NULL));
return true;
}
static bool ParseModConfiguration(char *levelName, json_value *root) {
if( root == NULL || root->type != json_object ) {
return false;
}
// parsing default configs
ParseLevelConfiguration(GetJsonField(root, json_object, "default", NULL));
// parsing level specific configs
json_value* levels = GetJsonField(root, json_array, "levels", NULL);
if( levels ) ParseLevelConfiguration(GetJsonObjectByStringField(levels, "filename", levelName, false, NULL));
return true;
}
void UnloadModConfiguration() {
if( ModConfig.semitrans.animtex ) {
free(ModConfig.semitrans.animtex);
ModConfig.semitrans.animtex = NULL;
}
FreePolyfilterNodes(&ModConfig.semitrans.rooms);
FreePolyfilterNodes(&ModConfig.semitrans.statics);
FreePolyfilterNodes(&ModConfig.reflect.statics);
for( DWORD i=0; i .
*/
#ifndef MOD_UTILS_H_INCLUDED
#define MOD_UTILS_H_INCLUDED
#include "global/types.h"
// Filter is presented by an array of poly index and polys number (starting from the index).
// The filter must be always terminated by an index 0.
// If the first item has index=~0 then there are no polys of such type to process.
// If the first item has index=0 and number=0 then all polys of such type must be processed.
#define POLYFILTER_SIZE 256
typedef struct {__int16 idx; __int16 num;} POLYINDEX;
typedef struct {
__int16 n_vtx, n_gt4, n_gt3, n_g4, n_g3;
POLYINDEX gt4[POLYFILTER_SIZE];
POLYINDEX gt3[POLYFILTER_SIZE];
POLYINDEX g4[POLYFILTER_SIZE];
POLYINDEX g3[POLYFILTER_SIZE];
} POLYFILTER;
typedef struct PolyfilterNode_t {
int id;
POLYFILTER filter;
struct PolyfilterNode_t *next;
} POLYFILTER_NODE;
typedef bool (*ENUM_POLYS_CB) (__int16 *ptrObj, int vtxCount, bool colored, LPVOID param);
/*
* Function list
*/
bool EnumeratePolys(__int16 *ptrObj, bool isRoomMesh, ENUM_POLYS_CB callback, POLYFILTER *filter, LPVOID param);
#ifdef FEATURE_MOD_CONFIG
bool IsModConfigLoaded();
bool IsModBarefoot();
const char *GetModLoadingPix();
DWORD GetModWaterColor();
bool IsModSemitransConfigLoaded();
POLYINDEX *GetModSemitransAnimtexFilter();
POLYFILTER_NODE *GetModSemitransRoomsFilter();
POLYFILTER_NODE *GetModSemitransStaticsFilter();
POLYFILTER_NODE **GetModSemitransObjectsFilter();
bool IsModReflectConfigLoaded();
POLYFILTER_NODE *GetModReflectStaticsFilter();
POLYFILTER_NODE **GetModReflectObjectsFilter();
void UnloadModConfiguration();
bool LoadModConfiguration(LPCTSTR levelFilePath);
#endif // FEATURE_MOD_CONFIG
#endif // MOD_UTILS_H_INCLUDED
================================================
FILE: modding/pause.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "modding/pause.h"
#include "game/health.h"
#include "game/inventory.h"
#include "game/invtext.h"
#include "game/sound.h"
#include "game/text.h"
#include "specific/display.h"
#include "specific/input.h"
#include "specific/output.h"
#include "specific/sndpc.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/joy_output.h"
#endif // FEATURE_INPUT_IMPROVED
#ifdef FEATURE_BACKGROUND_IMPROVED
static TEXT_STR_INFO *PausedText = NULL;
static void RemovePausedText() {
T_RemovePrint(PausedText);
PausedText = NULL;
}
static void DisplayPausedText() {
if( PausedText == NULL ) {
PausedText = T_Print(0, -24, 5, "Paused");
T_CentreH(PausedText, 1);
T_BottomAlign(PausedText, 1);
}
}
static int DisplayPauseRequester(const char *header, const char *option1, const char *option2, UINT16 selected) {
static bool isPauseTextReady = false;
if( !isPauseTextReady ) {
StatsRequester.reqFlags &= ~REQFLAG_NOCURSOR;
SetPCRequesterSize(&StatsRequester, 2, -48);
StatsRequester.lineHeight = 18;
StatsRequester.itemsCount = 0;
StatsRequester.selected = selected;
StatsRequester.lineOffset = 0;
StatsRequester.lineOldOffset = 0;
StatsRequester.pixWidth = 100;
StatsRequester.xPos = 0;
StatsRequester.zPos = 0;
StatsRequester.lpItemStrings1 = (char *)SaveGameStrings1;
StatsRequester.lpItemStrings2 = (char *)SaveGameStrings2;
StatsRequester.itemStringLen = 50;
Init_Requester(&StatsRequester);
SetRequesterHeading(&StatsRequester, header, 0, NULL, 0);
memset(SaveGameStrings1, 0, sizeof(SaveGameStrings1));
memset(SaveGameStrings2, 0, sizeof(SaveGameStrings2));
AddRequesterItem(&StatsRequester, option1, 0, NULL, 0);
AddRequesterItem(&StatsRequester, option2, 0, NULL, 0);
isPauseTextReady = true;
InputDB = 0;
InputStatus = 0;
}
int select = Display_Requester(&StatsRequester, 0, 0);
if( select > 0 ) {
isPauseTextReady = false;
} else {
InputDB = 0;
InputStatus = 0;
}
return select;
}
static int PauseRequester() {
static int state = 0;
int select = 0;
InputDB = GetDebouncedInput(InputStatus);
switch( state ) {
case 0:
if( CHK_ANY(InputDB, IN_PAUSE) ) {
select = 1;
break;
}
if( CHK_ANY(InputDB, IN_OPTION) ) {
state = 1;
}
if( state != 1 ) break;
// fall through
case 1:
switch( DisplayPauseRequester(CHK_ANY(GF_GameFlow.flags, GFF_DemoVersion)? "Exit Demo?" : "Exit to title?", "Continue", "Quit", 0) ) {
case 1: select = 1; break;
case 2: state = 2; break;
};
if( state != 2 ) break;
// fall through
case 2:
switch( DisplayPauseRequester("Are you sure?", "Yes", "No", 1) ) {
case 1: select = -1; break;
case 2: select = 1; break;
};
break;
}
if( select ) state = 0;
return select;
}
bool S_Pause() {
int oldOverlayStatus = OverlayStatus;
OverlayStatus = -3;
InventoryMode = INV_PauseMode;
T_RemovePrint(AmmoTextInfo);
AmmoTextInfo = NULL;
S_FadeInInventory(TRUE);
SOUND_Stop();
S_CDVolume(0);
TempVideoAdjust(HiRes, 1.0);
IsVidModeLock = true;
S_SetupAboveWater(FALSE);
int select = 0;
do {
S_InitialisePolyList(FALSE);
DoInventoryBackground();
DisplayPausedText();
DrawModeInfo();
T_DrawText();
S_OutputPolyList();
SOUND_EndScene();
S_DumpScreen();
#ifdef FEATURE_INPUT_IMPROVED
UpdateJoyOutput(false);
#endif // FEATURE_INPUT_IMPROVED
} while( !S_UpdateInput() && 0 == (select=PauseRequester()) );
Remove_Requester(&StatsRequester);
RemovePausedText();
IsVidModeLock = false;
TempVideoRemove();
#ifdef FEATURE_AUDIO_IMPROVED
if( Camera.underwater ) {
extern double UnderwaterMusicMute;
double volume = (1.0 - UnderwaterMusicMute) * (double)(MusicVolume * 25 + 5);
if( volume >= 1.0 ) {
S_CDVolume((DWORD)volume);
} else {
S_CDVolume(0);
}
} else {
S_CDVolume(MusicVolume * 25 + 5);
}
#else // FEATURE_AUDIO_IMPROVED
S_CDVolume(MusicVolume * 25 + 5);
#endif // FEATURE_AUDIO_IMPROVED
S_FadeOutInventory(TRUE);
InventoryChosen = ID_PASSPORT_OPTION;
InventoryExtraData[0] = 2; // set the last passport page in case if "Exit to Title" is selected
InventoryMode = INV_GameMode;
OverlayStatus = oldOverlayStatus;
return (select < 0);
}
#endif // FEATURE_BACKGROUND_IMPROVED
================================================
FILE: modding/pause.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef PAUSE_H_INCLUDED
#define PAUSE_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#ifdef FEATURE_BACKGROUND_IMPROVED
bool S_Pause();
#endif // FEATURE_BACKGROUND_IMPROVED
#endif // PAUSE_H_INCLUDED
================================================
FILE: modding/psx_bar.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "modding/psx_bar.h"
#include "specific/hwr.h"
#include "global/vars.h"
#ifdef FEATURE_HUD_IMPROVED
extern DWORD HealthBarMode;
static D3DCOLOR InterpolateColor(D3DCOLOR color0, D3DCOLOR color1, DWORD value, DWORD range) {
if( value == 0 )
return color0;
if( value == range )
return color1;
D3DCOLOR result = 0;
BYTE *c0 = (BYTE *)&color0;
BYTE *c1 = (BYTE *)&color1;
BYTE *res = (BYTE *)&result;
for( int i=0; i<4; ++i ) {
res[i] = (DWORD)c0[i]*(range-value)/range + (DWORD)c1[i]*value/range;
}
return result;
}
static void __cdecl DrawCololoredRect(float sx0, float sy0, float sx1, float sy1, float z, D3DCOLOR color0, D3DCOLOR color1, D3DCOLOR color2, D3DCOLOR color3, BYTE alpha) {
double sz, rhw;
D3DTLVERTEX vertex[4];
if( z > PhdFarZ ) {
return;
}
if( z < PhdNearZ ) {
z = PhdNearZ;
}
rhw = RhwFactor / (double)z;
sz = FltResZBuf - rhw * FltResZORhw;
vertex[0].sx = sx0;
vertex[0].sy = sy0;
vertex[0].color = RGBA_SETALPHA(color0, alpha);
vertex[1].sx = sx1;
vertex[1].sy = sy0;
vertex[1].color = RGBA_SETALPHA(color1, alpha);
vertex[2].sx = sx0;
vertex[2].sy = sy1;
vertex[2].color = RGBA_SETALPHA(color2, alpha);
vertex[3].sx = sx1;
vertex[3].sy = sy1;
vertex[3].color = RGBA_SETALPHA(color3, alpha);
for( int i=0; i<4; ++i ) {
vertex[i].sz = sz;
vertex[i].rhw = rhw;
vertex[i].specular = 0;
}
HWR_TexSource(0);
HWR_EnableColorKey(true);
HWR_DrawPrimitive(D3DPT_TRIANGLESTRIP, &vertex, 4, true);
}
static void PSX_DrawBar(int x0, int y0, int x1, int y1, int bar, int pixel, D3DCOLOR *left, D3DCOLOR *right, D3DCOLOR *frame, BYTE alpha) {
// Extra frame (dark gray)
if( HealthBarMode != 1 ) // skip extra frame if required
DrawCololoredRect(x0-pixel*3, y0-pixel*1, x1+pixel*3, y1+pixel*1, PhdNearZ + 40, frame[4], frame[5], frame[5], frame[4], 255);
// Outer frame (light gray)
DrawCololoredRect(x0-pixel*2, y0-pixel*2, x1+pixel*2, y1+pixel*2, PhdNearZ + 30, frame[2], frame[3], frame[3], frame[2], 255);
// Inner frame (black)
DrawCololoredRect(x0-pixel*1, y0-pixel*1, x1+pixel*1, y1+pixel*1, PhdNearZ + 20, frame[0], frame[1], frame[1], frame[0], 255);
// The bar
if( bar > 0 ) {
int i;
int dy[4];
D3DCOLOR dl[4], dr[4];
int dh = (y1 - y0) - pixel * 2;
for( i=0; i<4; ++i ) {
dy[i] = dh * i / 3;
if( i>0 && i<3 ) {
dl[i] = InterpolateColor(left[i-1], left[i], dy[i], dh);
dr[i] = InterpolateColor(right[i-1], right[i], dy[i], dh);
} else {
dl[i] = left[i];
dr[i] = right[i];
}
}
for( i=0; i<3; ++i ) {
DrawCololoredRect(x0, y1-dy[i+1], x0+bar, y1-dy[i], PhdNearZ + 10, dl[i+1], dr[i+1], dl[i], dr[i], alpha);
}
DrawCololoredRect(x0, y0+pixel*0, x0+bar, y0+pixel*1, PhdNearZ + 10, left[2], right[2], left[3], right[3], alpha);
DrawCololoredRect(x0, y0+pixel*1, x0+bar, y0+pixel*2, PhdNearZ + 10, left[5], right[5], left[4], right[4], alpha);
}
}
void __cdecl PSX_DrawHealthBar(int x0, int y0, int x1, int y1, int bar, int pixel, int alpha) {
D3DCOLOR left[6] = {0xFF680000, 0xFF700000, 0xFF980000, 0xFFD80000, 0xFFE40000, 0xFFF00000};
D3DCOLOR right[6] = {0xFF004400, 0xFF007400, 0xFF009C00, 0xFF00D400, 0xFF00E800, 0xFF00FC00};
D3DCOLOR frame[6] = {0xFF000000, 0xFF000000, 0xFF508484, 0xFFA0A0A0, 0xFF284242, 0xFF505050};
for( int i=0; i<6; ++i )
right[i] = InterpolateColor(left[i], right[i], bar, x1-x0);
CLAMP(alpha, 0, 255);
PSX_DrawBar(x0, y0, x1, y1, bar, pixel, left, right, frame, alpha);
}
void __cdecl PSX_DrawAirBar(int x0, int y0, int x1, int y1, int bar, int pixel, int alpha) {
D3DCOLOR left[6] = {0xFF004054, 0xFF005064, 0xFF006874, 0xFF007884, 0xFF00848E, 0xFF009098};
D3DCOLOR right[6] = {0xFF004000, 0xFF005000, 0xFF006800, 0xFF007800, 0xFF008400, 0xFF009000};
D3DCOLOR frame[6] = {0xFF000000, 0xFF000000, 0xFF508484, 0xFFA0A0A0, 0xFF284242, 0xFF505050};
for( int i=0; i<6; ++i )
right[i] = InterpolateColor(left[i], right[i], bar, x1-x0);
CLAMP(alpha, 0, 255);
PSX_DrawBar(x0, y0, x1, y1, bar, pixel, left, right, frame, alpha);
}
static void PSX_InsertBar(int polytype, int x0, int y0, int x1, int y1, int bar, int pixel, int alpha) {
CLAMP(alpha, 0, 255);
Sort3dPtr->_0 = (DWORD)Info3dPtr;
Sort3dPtr->_1 = (DWORD)PhdNearZ;
++Sort3dPtr;
++SurfaceCount;
*(Info3dPtr++) = polytype;
*(Info3dPtr++) = x0;
*(Info3dPtr++) = y0;
*(Info3dPtr++) = x1;
*(Info3dPtr++) = y1;
*(Info3dPtr++) = bar;
*(Info3dPtr++) = pixel;
*(Info3dPtr++) = alpha;
}
void __cdecl PSX_InsertHealthBar(int x0, int y0, int x1, int y1, int bar, int pixel, int alpha) {
PSX_InsertBar(POLY_HWR_healthbar, x0, y0, x1, y1, bar, pixel, alpha);
}
void __cdecl PSX_InsertAirBar(int x0, int y0, int x1, int y1, int bar, int pixel, int alpha) {
PSX_InsertBar(POLY_HWR_airbar, x0, y0, x1, y1, bar, pixel, alpha);
}
#endif // FEATURE_HUD_IMPROVED
================================================
FILE: modding/psx_bar.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef PSX_BAR_H_INCLUDED
#define PSX_BAR_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl PSX_DrawHealthBar(int x0, int y0, int x1, int y1, int bar, int pixel, int alpha);
void __cdecl PSX_DrawAirBar(int x0, int y0, int x1, int y1, int bar, int pixel, int alpha);
void __cdecl PSX_InsertHealthBar(int x0, int y0, int x1, int y1, int bar, int pixel, int alpha);
void __cdecl PSX_InsertAirBar(int x0, int y0, int x1, int y1, int bar, int pixel, int alpha);
#endif // PSX_BAR_H_INCLUDED
================================================
FILE: modding/raw_input.cpp
================================================
/*
* Copyright (c) 2017-2023 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "modding/raw_input.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#include
#include
#ifdef __cplusplus
} /* extern "C" */
#endif
#include "global/md5.h"
#define SONY_BT_REPORT_SIZE (78)
#define VID_SONY (0x054C)
#define PID_DUALSHOCK4_1 (0x05C4)
#define PID_DUALSHOCK4_2 (0x09CC)
#define PID_DUALSHOCK4_W (0x0BA0)
#define PID_DUALSENSE (0x0CE6)
#define VIA_USB " - USB"
#define VIA_BLUETOOTH " - Bluetooth"
#define NAME_DUALSHOCK4_1 "Sony DualShock 4 (1st gen)"
#define NAME_DUALSHOCK4_2 "Sony DualShock 4 (2nd gen)"
#define NAME_DUALSHOCK4_W "Sony DualShock 4 (adaptor)"
#define NAME_DUALSENSE "Sony DualSense"
typedef struct {
UINT numButtons;
bool buttons[32];
LONG rangeX, valueX;
LONG rangeY, valueY;
LONG rangeZ, valueZ;
LONG rangeRX, valueRX;
LONG rangeRY, valueRY;
LONG rangeRZ, valueRZ;
LONG rangeDP, valueDP;
} RAW_STATE;
typedef struct {
WORD leftMotor;
WORD rightMotor;
DWORD color;
bool updated;
} RAW_REPORT;
static DWORD SonyBluetoothCRC(LPCBYTE buf, DWORD len, DWORD crc) {
static const DWORD lut[] = {
0xD202EF8D, 0xA505DF1B, 0x3C0C8EA1, 0x4B0BBE37, 0xD56F2B94, 0xA2681B02, 0x3B614AB8, 0x4C667A2E,
0xDCD967BF, 0xABDE5729, 0x32D70693, 0x45D03605, 0xDBB4A3A6, 0xACB39330, 0x35BAC28A, 0x42BDF21C,
0xCFB5FFE9, 0xB8B2CF7F, 0x21BB9EC5, 0x56BCAE53, 0xC8D83BF0, 0xBFDF0B66, 0x26D65ADC, 0x51D16A4A,
0xC16E77DB, 0xB669474D, 0x2F6016F7, 0x58672661, 0xC603B3C2, 0xB1048354, 0x280DD2EE, 0x5F0AE278,
0xE96CCF45, 0x9E6BFFD3, 0x0762AE69, 0x70659EFF, 0xEE010B5C, 0x99063BCA, 0x000F6A70, 0x77085AE6,
0xE7B74777, 0x90B077E1, 0x09B9265B, 0x7EBE16CD, 0xE0DA836E, 0x97DDB3F8, 0x0ED4E242, 0x79D3D2D4,
0xF4DBDF21, 0x83DCEFB7, 0x1AD5BE0D, 0x6DD28E9B, 0xF3B61B38, 0x84B12BAE, 0x1DB87A14, 0x6ABF4A82,
0xFA005713, 0x8D076785, 0x140E363F, 0x630906A9, 0xFD6D930A, 0x8A6AA39C, 0x1363F226, 0x6464C2B0,
0xA4DEAE1D, 0xD3D99E8B, 0x4AD0CF31, 0x3DD7FFA7, 0xA3B36A04, 0xD4B45A92, 0x4DBD0B28, 0x3ABA3BBE,
0xAA05262F, 0xDD0216B9, 0x440B4703, 0x330C7795, 0xAD68E236, 0xDA6FD2A0, 0x4366831A, 0x3461B38C,
0xB969BE79, 0xCE6E8EEF, 0x5767DF55, 0x2060EFC3, 0xBE047A60, 0xC9034AF6, 0x500A1B4C, 0x270D2BDA,
0xB7B2364B, 0xC0B506DD, 0x59BC5767, 0x2EBB67F1, 0xB0DFF252, 0xC7D8C2C4, 0x5ED1937E, 0x29D6A3E8,
0x9FB08ED5, 0xE8B7BE43, 0x71BEEFF9, 0x06B9DF6F, 0x98DD4ACC, 0xEFDA7A5A, 0x76D32BE0, 0x01D41B76,
0x916B06E7, 0xE66C3671, 0x7F6567CB, 0x0862575D, 0x9606C2FE, 0xE101F268, 0x7808A3D2, 0x0F0F9344,
0x82079EB1, 0xF500AE27, 0x6C09FF9D, 0x1B0ECF0B, 0x856A5AA8, 0xF26D6A3E, 0x6B643B84, 0x1C630B12,
0x8CDC1683, 0xFBDB2615, 0x62D277AF, 0x15D54739, 0x8BB1D29A, 0xFCB6E20C, 0x65BFB3B6, 0x12B88320,
0x3FBA6CAD, 0x48BD5C3B, 0xD1B40D81, 0xA6B33D17, 0x38D7A8B4, 0x4FD09822, 0xD6D9C998, 0xA1DEF90E,
0x3161E49F, 0x4666D409, 0xDF6F85B3, 0xA868B525, 0x360C2086, 0x410B1010, 0xD80241AA, 0xAF05713C,
0x220D7CC9, 0x550A4C5F, 0xCC031DE5, 0xBB042D73, 0x2560B8D0, 0x52678846, 0xCB6ED9FC, 0xBC69E96A,
0x2CD6F4FB, 0x5BD1C46D, 0xC2D895D7, 0xB5DFA541, 0x2BBB30E2, 0x5CBC0074, 0xC5B551CE, 0xB2B26158,
0x04D44C65, 0x73D37CF3, 0xEADA2D49, 0x9DDD1DDF, 0x03B9887C, 0x74BEB8EA, 0xEDB7E950, 0x9AB0D9C6,
0x0A0FC457, 0x7D08F4C1, 0xE401A57B, 0x930695ED, 0x0D62004E, 0x7A6530D8, 0xE36C6162, 0x946B51F4,
0x19635C01, 0x6E646C97, 0xF76D3D2D, 0x806A0DBB, 0x1E0E9818, 0x6909A88E, 0xF000F934, 0x8707C9A2,
0x17B8D433, 0x60BFE4A5, 0xF9B6B51F, 0x8EB18589, 0x10D5102A, 0x67D220BC, 0xFEDB7106, 0x89DC4190,
0x49662D3D, 0x3E611DAB, 0xA7684C11, 0xD06F7C87, 0x4E0BE924, 0x390CD9B2, 0xA0058808, 0xD702B89E,
0x47BDA50F, 0x30BA9599, 0xA9B3C423, 0xDEB4F4B5, 0x40D06116, 0x37D75180, 0xAEDE003A, 0xD9D930AC,
0x54D13D59, 0x23D60DCF, 0xBADF5C75, 0xCDD86CE3, 0x53BCF940, 0x24BBC9D6, 0xBDB2986C, 0xCAB5A8FA,
0x5A0AB56B, 0x2D0D85FD, 0xB404D447, 0xC303E4D1, 0x5D677172, 0x2A6041E4, 0xB369105E, 0xC46E20C8,
0x72080DF5, 0x050F3D63, 0x9C066CD9, 0xEB015C4F, 0x7565C9EC, 0x0262F97A, 0x9B6BA8C0, 0xEC6C9856,
0x7CD385C7, 0x0BD4B551, 0x92DDE4EB, 0xE5DAD47D, 0x7BBE41DE, 0x0CB97148, 0x95B020F2, 0xE2B71064,
0x6FBF1D91, 0x18B82D07, 0x81B17CBD, 0xF6B64C2B, 0x68D2D988, 0x1FD5E91E, 0x86DCB8A4, 0xF1DB8832,
0x616495A3, 0x1663A535, 0x8F6AF48F, 0xF86DC419, 0x660951BA, 0x110E612C, 0x88073096, 0xFF000000,
};
for( DWORD i = 0; i < len; ++i ) {
crc = lut[((BYTE)crc) ^ ((BYTE)buf[i])] ^ (crc >> 8);
}
return crc;
}
static bool IsRawBluetooth(PHIDD_ATTRIBUTES pAttr, PHIDP_CAPS pCaps) {
if( !pAttr || !pCaps ) return false;
switch( pAttr->VendorID ) {
case VID_SONY:
switch( pAttr->ProductID ) {
case PID_DUALSHOCK4_1:
case PID_DUALSHOCK4_2:
case PID_DUALSHOCK4_W:
case PID_DUALSENSE:
if( pCaps->InputReportByteLength >= SONY_BT_REPORT_SIZE &&
pCaps->OutputReportByteLength >= SONY_BT_REPORT_SIZE )
{
return true;
}
break;
default:
break;
}
break;
default:
break;
}
return false;
}
static bool CalculateRawGUID(LPCTSTR lpString, LPGUID lpGuid) {
if( !lpString || !*lpString || !lpGuid ) return false;
// use MD5 hash as RawInput Device GUID
MD5_CTX mdContext;
MD5Init(&mdContext);
MD5Update(&mdContext, (unsigned char *)lpString, sizeof(TCHAR)*strlen(lpString));
MD5Final(&mdContext);
memcpy(lpGuid, mdContext.digest, sizeof(GUID));
return true;
}
LPCTSTR GetRawInputName(WORD vid, WORD pid, BOOL bt) {
switch( vid ) {
case VID_SONY:
switch( pid ) {
case PID_DUALSHOCK4_1:
return bt ? NAME_DUALSHOCK4_1 VIA_BLUETOOTH : NAME_DUALSHOCK4_1 VIA_USB;
case PID_DUALSHOCK4_2:
return bt ? NAME_DUALSHOCK4_2 VIA_BLUETOOTH : NAME_DUALSHOCK4_2 VIA_USB;
case PID_DUALSHOCK4_W:
return bt ? NAME_DUALSHOCK4_W VIA_BLUETOOTH : NAME_DUALSHOCK4_W VIA_USB;
case PID_DUALSENSE:
return bt ? NAME_DUALSENSE VIA_BLUETOOTH : NAME_DUALSENSE VIA_USB;
default:
break;
}
break;
default:
break;
}
return NULL;
}
bool RawInputEnumerate(BOOL(CALLBACK *callback)(LPGUID, LPCTSTR, LPCTSTR, WORD, WORD, LPVOID), LPVOID lpContext) {
if( callback == NULL ) return false;
GUID hidGuid;
HidD_GetHidGuid(&hidGuid);
HANDLE hDevs = SetupDiGetClassDevs(&hidGuid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if( hDevs == INVALID_HANDLE_VALUE ) return false;
DWORD devIndex = 0;
SP_DEVINFO_DATA devInfo;
devInfo.cbSize = sizeof(SP_DEVINFO_DATA);
while( SetupDiEnumDeviceInfo(hDevs, devIndex++, &devInfo) ) {
DWORD ifaceIndex = 0;
SP_DEVICE_INTERFACE_DATA ifaceInfo;
ifaceInfo.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
while( SetupDiEnumDeviceInterfaces(hDevs, &devInfo, &hidGuid, ifaceIndex++, &ifaceInfo) ) {
DWORD detailSize = 0;
SetupDiGetDeviceInterfaceDetail(hDevs, &ifaceInfo, NULL, 0, &detailSize, NULL);
PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(detailSize);
pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
SetupDiGetDeviceInterfaceDetail(hDevs, &ifaceInfo, pDetail, detailSize, NULL, NULL);
HANDLE hDevice = CreateFile(pDetail->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if( hDevice != INVALID_HANDLE_VALUE ) {
PHIDP_PREPARSED_DATA pPreparsed = NULL;
HIDD_ATTRIBUTES attr;
HIDP_CAPS caps;
bool fetched = (HidD_GetAttributes(hDevice, &attr)
&& HidD_GetPreparsedData(hDevice, &pPreparsed)
&& HIDP_STATUS_SUCCESS == HidP_GetCaps(pPreparsed, &caps));
if( pPreparsed != NULL ) HidD_FreePreparsedData(pPreparsed);
CloseHandle(hDevice);
if( fetched && caps.UsagePage == HID_USAGE_PAGE_GENERIC
&& (caps.Usage == HID_USAGE_GENERIC_JOYSTICK || caps.Usage == HID_USAGE_GENERIC_GAMEPAD) )
{
GUID deviceGuid;
LPCTSTR productName = GetRawInputName(attr.VendorID, attr.ProductID, IsRawBluetooth(&attr, &caps));
if( productName != NULL && CalculateRawGUID(pDetail->DevicePath, &deviceGuid) ) {
callback(&deviceGuid, pDetail->DevicePath, productName, attr.VendorID, attr.ProductID, lpContext);
}
}
}
free(pDetail);
}
}
SetupDiDestroyDeviceInfoList(hDevs);
return true;
}
// --------------------
// RAW HID Device class
// --------------------
class RawHidDevice {
private:
// fields
bool isStop = false;
bool isInit = false;
bool isConnected = false;
bool isBluetooth = false;
HANDLE hMutex = NULL;
HANDLE hThread = NULL;
LPTSTR lpDeviceName = NULL;
HANDLE hDeviceFile = INVALID_HANDLE_VALUE;
PHIDP_PREPARSED_DATA pPreparsedData = NULL;
PHIDP_BUTTON_CAPS pButtonCaps = NULL;
PHIDP_VALUE_CAPS pValueCaps = NULL;
HIDD_ATTRIBUTES HidAttr;
HIDP_CAPS HidCaps;
LPBYTE pRawInput = NULL;
RAW_STATE RawState;
RAW_REPORT RawReport;
// methods
static DWORD WINAPI StaticTask(CONST LPVOID lpParam) {
RawHidDevice *This = (RawHidDevice *)lpParam;
return This->Task();
}
DWORD Task();
bool Connect();
void Disconnect(bool release=false);
bool Reconnect();
bool Send(LPCVOID lpBuffer, DWORD nSize);
bool Receive(LPVOID lpBuffer, DWORD nSize);
bool Calibrate();
bool SonyDualSenseRumbleAdjust(int *pLeftMotor, int *pRightMotor, int *pIntensity);
bool SonyControllerReport(LPBYTE buf, DWORD bufLen, DWORD productId, RAW_REPORT *pReport);
bool ParseRawInputStandard(LPBYTE buf, DWORD bufLen, RAW_STATE *pState);
bool ParseRawInputSonyDualShock4(LPBYTE buf, DWORD bufLen, RAW_STATE *pState);
bool ParseRawInputSonyDualSense(LPBYTE buf, DWORD bufLen, RAW_STATE *pState);
bool SendRawReport(bool clean=false);
bool ReceiveRawInput();
public:
bool IsRunning();
bool Start(LPCTSTR lpName);
void Stop();
bool SetState(WORD leftMotor, WORD rightMotor, DWORD colors);
bool GetState(RINPUT_STATE *pState);
};
DWORD RawHidDevice::Task() {
Connect();
while( !isStop ) {
if( isConnected ) {
ReceiveRawInput();
SendRawReport();
} else if( !Reconnect() ) {
Sleep(100);
}
}
Disconnect(true);
return 0;
}
bool RawHidDevice::Connect() {
if( hDeviceFile == INVALID_HANDLE_VALUE ) {
hDeviceFile = CreateFile(lpDeviceName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
}
isConnected = (hDeviceFile != INVALID_HANDLE_VALUE);
if( isConnected && !isInit ) {
HANDLE hHeap = GetProcessHeap();
if( !HidD_GetAttributes(hDeviceFile, &HidAttr) ||
!HidD_GetPreparsedData(hDeviceFile, &pPreparsedData) ||
HIDP_STATUS_SUCCESS != HidP_GetCaps(pPreparsedData, &HidCaps) )
{
Disconnect(true);
return false;
}
isBluetooth = IsRawBluetooth(&HidAttr, &HidCaps);
pRawInput = (LPBYTE)HeapAlloc(hHeap, 0, HidCaps.InputReportByteLength);
pButtonCaps = (PHIDP_BUTTON_CAPS)HeapAlloc(hHeap, 0, sizeof(HIDP_BUTTON_CAPS) * HidCaps.NumberInputButtonCaps);
pValueCaps = (PHIDP_VALUE_CAPS)HeapAlloc(hHeap, 0, sizeof(HIDP_VALUE_CAPS) * HidCaps.NumberInputValueCaps);
isInit = true;
}
if( !isConnected ) {
return false;
}
Calibrate();
if( isBluetooth ) {
SendRawReport(true);
}
return true;
}
void RawHidDevice::Disconnect(bool release) {
if( release ) {
HANDLE hHeap = GetProcessHeap();
if( pPreparsedData != NULL ) {
HidD_FreePreparsedData(pPreparsedData);
pPreparsedData = NULL;
}
if( pButtonCaps != NULL ) {
HeapFree(hHeap, 0, pButtonCaps);
pButtonCaps = NULL;
}
if( pValueCaps != NULL ) {
HeapFree(hHeap, 0, pValueCaps);
pValueCaps = NULL;
}
if( pRawInput != NULL ) {
HeapFree(hHeap, 0, pRawInput);
pRawInput = NULL;
}
memset(&HidAttr, 0, sizeof(HidAttr));
memset(&HidCaps, 0, sizeof(HidCaps));
isInit = false;
}
if( hDeviceFile != INVALID_HANDLE_VALUE ) {
CloseHandle(hDeviceFile);
hDeviceFile = INVALID_HANDLE_VALUE;
}
isConnected = false;
}
bool RawHidDevice::Reconnect() {
Calibrate(); // kick OS to refresh lost device state
Disconnect();
return Connect();
}
bool RawHidDevice::Send(LPCVOID lpBuffer, DWORD nSize) {
OVERLAPPED overlapped;
memset(&overlapped, 0, sizeof(overlapped));
WriteFile(hDeviceFile, lpBuffer, nSize, NULL, &overlapped);
for( int i=0; i<50; ++i ) {
Sleep(1);
DWORD bytesWritten = 0;
BOOL res = GetOverlappedResult(hDeviceFile, &overlapped, &bytesWritten, FALSE);
if( res && bytesWritten >= nSize ) {
return true;
}
}
isConnected = false;
return false;
}
bool RawHidDevice::Receive(LPVOID lpBuffer, DWORD nSize) {
OVERLAPPED overlapped;
memset(&overlapped, 0, sizeof(overlapped));
ReadFile(hDeviceFile, lpBuffer, nSize, NULL, &overlapped);
for( int i=0; i<50; ++i ) {
Sleep(1);
DWORD bytesRead = 0;
BOOL res = GetOverlappedResult(hDeviceFile, &overlapped, &bytesRead, FALSE);
if( res && bytesRead >= nSize ) {
return true;
}
}
isConnected = false;
return false;
}
bool RawHidDevice::Calibrate() {
if( !isInit || hDeviceFile == INVALID_HANDLE_VALUE ) return false;
// We don't need the feature data itself, we need this to unlock advanced controls
bool result = true;
switch( HidAttr.VendorID ) {
case VID_SONY:
switch( HidAttr.ProductID ) {
case PID_DUALSHOCK4_1:
case PID_DUALSHOCK4_2:
case PID_DUALSHOCK4_W:
{
BYTE buf[41] = {0x02};
result = HidD_GetFeature(hDeviceFile, buf, sizeof(buf));
}
break;
case PID_DUALSENSE:
{
BYTE buf[41] = {0x05};
result = HidD_GetFeature(hDeviceFile, buf, sizeof(buf));
}
break;
default:
break;
}
break;
default:
break;
}
return result;
}
bool RawHidDevice::SonyDualSenseRumbleAdjust(int *pLeftMotor, int *pRightMotor, int *pIntensity) {
// Sony DualSense uses haptics instead of regular vibration motors.
// For legacy rumble emulation it scales rumble intensity very very bad,
// so you feel no difference between weak and strong vibrations.
// But there is a workaround with global intensity parameter which affects
// both motors, so this function decides how to scale every motor force
// and overall intensity.
if( pLeftMotor == NULL || pRightMotor == NULL || pIntensity == NULL ) {
return false;
}
int lrange = 0, lforce = 0;
int rrange = 0, rforce = 0;
if( *pLeftMotor ) {
// make left motor to be fully scaled
int motor = *pLeftMotor * 4;
lrange = (motor - 1) / 128;
CLAMPG(lrange, 6);
lforce = motor - lrange * 128;
CLAMP(lforce, 1, 255);
}
if( *pRightMotor ) {
// make right motor to be 3/4 scaled (minimum range is 2)
int motor = *pRightMotor * 3;
rrange = (motor - 1) / 128;
CLAMPG(rrange, 4);
rforce = motor - rrange * 128;
CLAMP(rforce, 1, 255);
rrange += 2;
}
if( lforce && rforce ) {
// don't use 'else if' here, it is supposed to be sequential 'if'
if( rrange > lrange && rforce < 128 ) {
rrange--;
rforce += 128;
}
if( lrange < rrange && lforce >= 128 ) {
lrange++;
lforce -= 127;
}
if( lrange > rrange && lforce < 128 ) {
lrange--;
lforce += 128;
}
if( rrange < lrange && rforce >= 128 ) {
rrange++;
rforce -= 127;
}
if( rrange != lrange ) {
// left (heavy) motor is leading
// fit the right (light) motor
rforce = rrange < lrange ? 1 : 255;
rrange = lrange;
}
}
*pLeftMotor = lforce;
*pRightMotor = rforce;
*pIntensity = 6 - (lforce > 0 ? lrange : rrange);
return true;
}
bool RawHidDevice::SonyControllerReport(LPBYTE buf, DWORD bufLen, DWORD productId, RAW_REPORT *pReport) {
LPBYTE data = NULL;
memset(buf, 0, bufLen);
switch( productId ) {
case PID_DUALSHOCK4_1:
case PID_DUALSHOCK4_2:
case PID_DUALSHOCK4_W:
if( isBluetooth ) {
buf[0] = 0x11;
buf[1] = 0xC0;
buf[2] = 0xA0;
data = buf+3;
} else if( bufLen == 32 ) {
buf[0] = 0x05;
data = buf+1;
} else {
return false;
}
if( pReport == NULL ) {
data[0] = 0xF4;
} else {
data[0] = 0xF7;
data[3] = (BYTE)(pReport->rightMotor>>8);
data[4] = (BYTE)(pReport->leftMotor>>8);
data[5] = (BYTE)(RGB_GETRED(pReport->color));
data[6] = (BYTE)(RGB_GETGREEN(pReport->color));
data[7] = (BYTE)(RGB_GETBLUE(pReport->color));
}
break;
case PID_DUALSENSE:
if( isBluetooth ) {
buf[0] = 0x31;
buf[1] = 0x02;
data = buf+2;
} else if( bufLen == 48 ) {
buf[0] = 0x02;
data = buf+1;
} else {
return false;
}
if( pReport == NULL ) {
data[1] = 0x15;
} else {
int leftMotor = pReport->leftMotor>>8;
int rightMotor = pReport->rightMotor>>8;
int intensity = 2;
SonyDualSenseRumbleAdjust(&leftMotor, &rightMotor, &intensity);
data[0] = 0x0F;
data[1] = 0x55;
data[2] = (BYTE)rightMotor;
data[3] = (BYTE)leftMotor;
data[8] = 0x00; // Mute LED: [Off]=0, [On]=1, [Pulse]=2
// 10 - 19: right trigger feedback
// 21 - 30: left trigger feedback
data[36] = (BYTE)intensity; // Haptic intensity: [High]=0, [Medium]=2, [Low]=5, [Tiny]=7
data[38] = 0x02; // LED mode: [LED static brightness in 42]=1, [Blue LED fade in 41]=2
data[41] = 0x02; // LED fade: [Fade in]=1, [Fade out]=2
data[42] = 0x02; // LED brightness: [High]=0, [Medium]=1, [Low]=2
data[43] = 0x00; // Player LED mask
data[44] = (BYTE)(RGB_GETRED(pReport->color));
data[45] = (BYTE)(RGB_GETGREEN(pReport->color));
data[46] = (BYTE)(RGB_GETBLUE(pReport->color));
}
break;
default:
return false;
}
if( isBluetooth ) {
DWORD crc = 0;
BYTE btheader[] = {0xA2}; // (format 4:2:2) transaction_type=0xA (data), parameters=0x0 (none), report_type=0x2 (output)
crc = SonyBluetoothCRC(btheader, sizeof(btheader), crc);
crc = SonyBluetoothCRC(buf, SONY_BT_REPORT_SIZE-4, crc);
for( int i=4; i>=1; --i ) {
buf[SONY_BT_REPORT_SIZE - i] = (BYTE)crc;
crc >>= 8;
}
}
return true;
}
bool RawHidDevice::ParseRawInputStandard(LPBYTE buf, DWORD bufLen, RAW_STATE *pState) {
WORD capsLen;
DWORD value;
USAGE usage[128];
if( pState == NULL || pPreparsedData == NULL || pButtonCaps == NULL || pValueCaps == NULL ) return false;
// Button caps
capsLen = HidCaps.NumberInputButtonCaps;
if( HIDP_STATUS_SUCCESS != HidP_GetButtonCaps(HidP_Input, pButtonCaps, &capsLen, pPreparsedData) ) return false;
pState->numButtons = pButtonCaps->Range.UsageMax - pButtonCaps->Range.UsageMin + 1;
// Button states
value = pState->numButtons;
if( HIDP_STATUS_SUCCESS != HidP_GetUsages(HidP_Input, pButtonCaps->UsagePage,
0, usage, &value, pPreparsedData, (PCHAR)buf, bufLen) ) return false;
memset(pState->buttons, 0, sizeof(pState->buttons));
for( UINT i = 0; i < MIN(value, 32); ++i ) {
pState->buttons[usage[i] - pButtonCaps->Range.UsageMin] = true;
}
// Axes and D-Pad caps
capsLen = HidCaps.NumberInputValueCaps;
if( HIDP_STATUS_SUCCESS != HidP_GetValueCaps(HidP_Input, pValueCaps, &capsLen, pPreparsedData) ) return false;
// Axes and D-Pad values
for( UINT i = 0; i < HidCaps.NumberInputValueCaps; ++i ) {
if( HIDP_STATUS_SUCCESS != HidP_GetUsageValue(HidP_Input, pValueCaps[i].UsagePage,
0, pValueCaps[i].Range.UsageMin, &value, pPreparsedData, (PCHAR)buf, bufLen) ) continue;
switch( pValueCaps[i].Range.UsageMin ) {
case '0': // X-axis
pState->valueX = (UWORD)value - pValueCaps[i].LogicalMin;
pState->rangeX = 1 + (UWORD)pValueCaps[i].LogicalMax - pValueCaps[i].LogicalMin;
break;
case '1': // Y-axis
pState->valueY = (UWORD)value - pValueCaps[i].LogicalMin;
pState->rangeY = 1 + (UWORD)pValueCaps[i].LogicalMax - pValueCaps[i].LogicalMin;
break;
case '2': // Z-axis
pState->valueZ = (UWORD)value - pValueCaps[i].LogicalMin;
pState->rangeZ = 1 + (UWORD)pValueCaps[i].LogicalMax - pValueCaps[i].LogicalMin;
break;
case '3': // RX-axis
pState->valueRX = (UWORD)value - pValueCaps[i].LogicalMin;
pState->rangeRX = 1 + (UWORD)pValueCaps[i].LogicalMax - pValueCaps[i].LogicalMin;
break;
case '4': // RY-axis
pState->valueRY = (UWORD)value - pValueCaps[i].LogicalMin;
pState->rangeRY = 1 + (UWORD)pValueCaps[i].LogicalMax - pValueCaps[i].LogicalMin;
break;
case '5': // RZ-axis
pState->valueRZ = (UWORD)value - pValueCaps[i].LogicalMin;
pState->rangeRZ = 1 + (UWORD)pValueCaps[i].LogicalMax - pValueCaps[i].LogicalMin;
break;
case '9': // D-Pad
if( value < (UWORD)pValueCaps[i].LogicalMin || value > (UWORD)pValueCaps[i].LogicalMax ) {
pState->valueDP = -1;
} else {
pState->valueDP = (UWORD)value - pValueCaps[i].LogicalMin;
}
pState->rangeDP = 1 + (UWORD)pValueCaps[i].LogicalMax - pValueCaps[i].LogicalMin;
break;
}
}
return true;
}
bool RawHidDevice::ParseRawInputSonyDualShock4(LPBYTE buf, DWORD bufLen, RAW_STATE *pState) {
if( buf == NULL || bufLen < 16 || pState == NULL ) return false;
memset(pState, 0, sizeof(RAW_STATE));
// Axes
pState->rangeX = pState->rangeY = pState->rangeZ = 0x100;
pState->rangeRX = pState->rangeRY = pState->rangeRZ = 0x100;
pState->valueX = buf[1];
pState->valueY = buf[2];
pState->valueZ = buf[3];
pState->valueRZ = buf[4];
pState->valueRX = buf[8];
pState->valueRY = buf[9];
// D-Pad
pState->rangeDP = 8;
pState->valueDP = (buf[5] & 0x0F);
if( pState->valueDP >= pState->rangeDP ) {
pState->valueDP = -1;
}
// Buttons
pState->numButtons = 14;
WORD mask = ((WORD)buf[5] >> 4) | ((WORD)buf[6] << 4) | ((WORD)buf[7] << 12);
for( UINT i = 0; i < pState->numButtons; ++i ) {
pState->buttons[i] = ((mask & (1<rangeX = pState->rangeY = pState->rangeZ = 0x100;
pState->rangeRX = pState->rangeRY = pState->rangeRZ = 0x100;
pState->valueX = buf[1];
pState->valueY = buf[2];
pState->valueZ = buf[3];
pState->valueRZ = buf[4];
pState->valueRX = buf[5];
pState->valueRY = buf[6];
// D-Pad
pState->rangeDP = 8;
pState->valueDP = (buf[8] & 0x0F);
if( pState->valueDP >= pState->rangeDP ) {
pState->valueDP = -1;
}
// Buttons
pState->numButtons = 14;
WORD mask = ((WORD)buf[8] >> 4) | ((WORD)buf[9] << 4) | ((WORD)buf[10] << 12);
for( UINT i = 0; i < pState->numButtons; ++i ) {
pState->buttons[i] = ((mask & (1<= 0 ) {
pState->dPad = 36000 * RawState.valueDP / RawState.rangeDP;
} else {
pState->dPad = -1;
}
pState->btnSquare = RawState.buttons[0];
pState->btnCross = RawState.buttons[1];
pState->btnCircle = RawState.buttons[2];
pState->btnTriangle = RawState.buttons[3];
pState->btnL1 = RawState.buttons[4];
pState->btnR1 = RawState.buttons[5];
pState->btnL2 = RawState.buttons[6];
pState->btnR2 = RawState.buttons[7];
pState->btnShare = RawState.buttons[8];
pState->btnOptions = RawState.buttons[9];
pState->btnL3 = RawState.buttons[10];
pState->btnR3 = RawState.buttons[11];
pState->btnPS = RawState.buttons[12];
pState->btnTouch = RawState.buttons[13];
if( RawState.rangeX ) pState->axisLX = (float)(RawState.valueX*2 - RawState.rangeX) / (float)RawState.rangeX;
if( RawState.rangeY ) pState->axisLY = (float)(RawState.rangeY - RawState.valueY*2) / (float)RawState.rangeY;
if( RawState.rangeZ ) pState->axisRX = (float)(RawState.valueZ*2 - RawState.rangeZ) / (float)RawState.rangeZ;
if( RawState.rangeRZ ) pState->axisRY = (float)(RawState.rangeRZ - RawState.valueRZ*2) / (float)RawState.rangeRZ;
if( RawState.rangeRX ) pState->axisL2 = (float)RawState.valueRX / (float)RawState.rangeRX;
if( RawState.rangeRY ) pState->axisR2 = (float)RawState.valueRY / (float)RawState.rangeRY;
ReleaseMutex(hMutex);
return true;
}
static RawHidDevice RawInput;
bool RawInputStart(LPCTSTR lpName) {
return RawInput.Start(lpName);
}
void RawInputStop() {
RawInput.Stop();
}
bool RawInputSetState(WORD leftMotor, WORD rightMotor, DWORD color) {
return RawInput.SetState(leftMotor, rightMotor, color);
}
bool RawInputGetState(RINPUT_STATE *pState) {
return RawInput.GetState(pState);
}
#endif // FEATURE_INPUT_IMPROVED
================================================
FILE: modding/raw_input.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef RAW_INPUT_H_INCLUDED
#define RAW_INPUT_H_INCLUDED
#include "global/types.h"
/*
* This whole RawInput implementation is created for Sony DualShock 4 / DualSense, so it uses PlayStation button names
*/
typedef struct {
LONG dPad; // -1 if unpressed, and 0-36000 if pressed
float axisLX, axisLY, axisRX, axisRY, axisL2, axisR2;
WORD btnSquare:1, btnCross:1, btnCircle:1, btnTriangle:1;
WORD btnL1:1, btnR1:1, btnL2:1, btnR2:1, btnL3:1, btnR3:1;
WORD btnShare:1, btnOptions:1, btnPS:1, btnTouch:1, btnReserved:2;
} RINPUT_STATE;
/*
* Function list
*/
LPCTSTR GetRawInputName(WORD vid, WORD pid, BOOL bt);
bool RawInputEnumerate(BOOL(CALLBACK *callback)(LPGUID, LPCTSTR, LPCTSTR, WORD, WORD, LPVOID), LPVOID lpContext);
bool RawInputStart(LPCTSTR lpName);
void RawInputStop();
bool RawInputSetState(WORD leftMotor, WORD rightMotor, DWORD color);
bool RawInputGetState(RINPUT_STATE *pState);
#endif // RAW_INPUT_H_INCLUDED
================================================
FILE: modding/texture_utils.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "modding/texture_utils.h"
#include "specific/init_input.h"
#include "specific/output.h"
#include "specific/texture.h"
#include "specific/utils.h"
#include "modding/file_utils.h"
#include "modding/json_utils.h"
#include "global/vars.h"
#ifdef FEATURE_HUD_IMPROVED
#define BTN_SPR_IDX (ARRAY_SIZE(PhdSpriteInfo) - 256)
DWORD JoystickButtonStyle = 0;
typedef enum {
kbd_esc,
kbd_f1,
kbd_f2,
kbd_f3,
kbd_f4,
kbd_f5,
kbd_f6,
kbd_f7,
kbd_f8,
kbd_f9,
kbd_f10,
kbd_f11,
kbd_f12,
kbd_tab,
kbd_1,
kbd_2,
kbd_3,
kbd_4,
kbd_5,
kbd_6,
kbd_7,
kbd_8,
kbd_9,
kbd_0,
kbd_minus,
kbd_equals,
kbd_backspace,
kbd_capslock,
kbd_q,
kbd_w,
kbd_e,
kbd_r,
kbd_t,
kbd_y,
kbd_u,
kbd_i,
kbd_o,
kbd_p,
kbd_lbracket,
kbd_rbracket,
kbd_return,
kbd_ctrl,
kbd_a,
kbd_s,
kbd_d,
kbd_f,
kbd_g,
kbd_h,
kbd_j,
kbd_k,
kbd_l,
kbd_semicolon,
kbd_apostrophe,
kbd_grave,
kbd_shift,
kbd_alt,
kbd_backslash,
kbd_z,
kbd_x,
kbd_c,
kbd_v,
kbd_b,
kbd_n,
kbd_m,
kbd_comma,
kbd_period,
kbd_slash,
kbd_oem102,
kbd_space,
kbd_printscreen,
kbd_scrolllock,
kbd_pause,
kbd_insert,
kbd_home,
kbd_pageup,
kbd_delete,
kbd_end,
kbd_pagedown,
kbd_left,
kbd_right,
kbd_up,
kbd_down,
kbd_numlock,
kbd_pad7,
kbd_pad8,
kbd_pad9,
kbd_divide,
kbd_pad4,
kbd_pad5,
kbd_pad6,
kbd_multiply,
kbd_pad1,
kbd_pad2,
kbd_pad3,
kbd_subtract,
kbd_pad0,
kbd_decimal,
kbd_enter,
kbd_add,
joy_di1, joy_di2, joy_di3, joy_di4, joy_di5, joy_di6, joy_di7, joy_di8,
joy_di9, joy_di10, joy_di11, joy_di12, joy_di13, joy_di14, joy_di15, joy_di16,
joy_ps1, joy_ps2, joy_ps3, joy_ps4, joy_ps5, joy_ps6, joy_ps7, joy_ps8,
joy_ps9, joy_ps10, joy_ps11, joy_ps12, joy_ps13, joy_ps14, joy_ps15, joy_ps16,
joy_xi1, joy_xi2, joy_xi3, joy_xi4, joy_xi5, joy_xi6, joy_xi7, joy_xi8,
joy_xi9, joy_xi10, joy_xi11, joy_xi12, joy_xi13, joy_xi14, joy_xi15, joy_xi16,
button_sprites_number,
} SPR_BUTTONS;
typedef struct {
int id;
const char *name;
} MAP;
static MAP btnMap[] = {
{kbd_esc, "esc"},
{kbd_f1, "f1"},
{kbd_f2, "f2"},
{kbd_f3, "f3"},
{kbd_f4, "f4"},
{kbd_f5, "f5"},
{kbd_f6, "f6"},
{kbd_f7, "f7"},
{kbd_f8, "f8"},
{kbd_f9, "f9"},
{kbd_f10, "f10"},
{kbd_f11, "f11"},
{kbd_f12, "f12"},
{kbd_tab, "tab"},
{kbd_capslock, "capslock"},
{kbd_shift, "shift"},
{kbd_ctrl, "ctrl"},
{kbd_alt, "alt"},
{kbd_space, "space"},
{kbd_backspace, "backspace"},
{kbd_return, "return"},
{kbd_1, "1"},
{kbd_2, "2"},
{kbd_3, "3"},
{kbd_4, "4"},
{kbd_5, "5"},
{kbd_6, "6"},
{kbd_7, "7"},
{kbd_8, "8"},
{kbd_9, "9"},
{kbd_0, "0"},
{kbd_minus, "-"},
{kbd_equals, "="},
{kbd_q, "q"},
{kbd_w, "w"},
{kbd_e, "e"},
{kbd_r, "r"},
{kbd_t, "t"},
{kbd_y, "y"},
{kbd_u, "u"},
{kbd_i, "i"},
{kbd_o, "o"},
{kbd_p, "p"},
{kbd_lbracket, "["},
{kbd_rbracket, "]"},
{kbd_a, "a"},
{kbd_s, "s"},
{kbd_d, "d"},
{kbd_f, "f"},
{kbd_g, "g"},
{kbd_h, "h"},
{kbd_j, "j"},
{kbd_k, "k"},
{kbd_l, "l"},
{kbd_semicolon, ";"},
{kbd_apostrophe, "'"},
{kbd_grave, "`"},
{kbd_backslash, "\\"},
{kbd_z, "z"},
{kbd_x, "x"},
{kbd_c, "c"},
{kbd_v, "v"},
{kbd_b, "b"},
{kbd_n, "n"},
{kbd_m, "m"},
{kbd_comma, ","},
{kbd_period, "."},
{kbd_slash, "/"},
{kbd_oem102, "<"},
{kbd_printscreen, "printscreen"},
{kbd_scrolllock, "scrolllock"},
{kbd_pause, "pause"},
{kbd_insert, "insert"},
{kbd_home, "home"},
{kbd_pageup, "pageup"},
{kbd_delete, "delete"},
{kbd_end, "end"},
{kbd_pagedown, "pagedown"},
{kbd_left, "left"},
{kbd_right, "right"},
{kbd_up, "up"},
{kbd_down, "down"},
{kbd_numlock, "numlock"},
{kbd_pad7, "pad7"},
{kbd_pad8, "pad8"},
{kbd_pad9, "pad9"},
{kbd_divide, "pad/"},
{kbd_pad4, "pad4"},
{kbd_pad5, "pad5"},
{kbd_pad6, "pad6"},
{kbd_multiply, "pad*"},
{kbd_pad1, "pad1"},
{kbd_pad2, "pad2"},
{kbd_pad3, "pad3"},
{kbd_subtract, "pad-"},
{kbd_pad0, "pad0"},
{kbd_decimal, "pad."},
{kbd_enter, "enter"},
{kbd_add, "pad+"},
{joy_di1, "di1"}, {joy_di2, "di2"}, {joy_di3, "di3"}, {joy_di4, "di4"},
{joy_di5, "di5"}, {joy_di6, "di6"}, {joy_di7, "di7"}, {joy_di8, "di8"},
{joy_di9, "di9"}, {joy_di10, "di10"}, {joy_di11, "di11"}, {joy_di12, "di12"},
{joy_di13, "di13"}, {joy_di14, "di14"}, {joy_di15, "di15"}, {joy_di16, "di16"},
{joy_ps1, "ps1"}, {joy_ps2, "ps2"}, {joy_ps3, "ps3"}, {joy_ps4, "ps4"},
{joy_ps5, "ps5"}, {joy_ps6, "ps6"}, {joy_ps7, "ps7"}, {joy_ps8, "ps8"},
{joy_ps9, "ps9"}, {joy_ps10, "ps10"}, {joy_ps11, "ps11"}, {joy_ps12, "ps12"},
{joy_ps13, "ps13"}, {joy_ps14, "ps14"}, {joy_ps15, "ps15"}, {joy_ps16, "ps16"},
{joy_xi1, "xi1"}, {joy_xi2, "xi2"}, {joy_xi3, "xi3"}, {joy_xi4, "xi4"},
{joy_xi5, "xi5"}, {joy_xi6, "xi6"}, {joy_xi7, "xi7"}, {joy_xi8, "xi8"},
{joy_xi9, "xi9"}, {joy_xi10, "xi10"}, {joy_xi11, "xi11"}, {joy_xi12, "xi12"},
{joy_xi13, "xi13"}, {joy_xi14, "xi14"}, {joy_xi15, "xi15"}, {joy_xi16, "xi16"},
};
static MAP joyMapDI[] = {
{joy_di1, "joy1"}, {joy_di2, "joy2"}, {joy_di3, "joy3"}, {joy_di4, "joy4"},
{joy_di5, "joy5"}, {joy_di6, "joy6"}, {joy_di7, "joy7"}, {joy_di8, "joy8"},
{joy_di9, "joy9"}, {joy_di10, "joy10"}, {joy_di11, "joy11"}, {joy_di12, "joy12"},
{joy_di13, "joy13"}, {joy_di14, "joy14"}, {joy_di15, "joy15"}, {joy_di16, "joy16"},
};
static MAP joyMapPS[] = {
{joy_ps1, "joy1"}, {joy_ps2, "joy2"}, {joy_ps3, "joy3"}, {joy_ps4, "joy4"},
{joy_ps5, "joy5"}, {joy_ps6, "joy6"}, {joy_ps7, "joy7"}, {joy_ps8, "joy8"},
{joy_ps9, "joy9"}, {joy_ps10, "joy10"}, {joy_ps11, "joy11"}, {joy_ps12, "joy12"},
{joy_ps13, "joy13"}, {joy_ps14, "joy14"}, {joy_ps15, "joy15"}, {joy_ps16, "joy16"},
};
static MAP joyMapXI[] = {
{joy_xi1, "joy1"}, {joy_xi2, "joy2"}, {joy_xi3, "joy3"}, {joy_xi4, "joy4"},
{joy_xi5, "joy5"}, {joy_xi6, "joy6"}, {joy_xi7, "joy7"}, {joy_xi8, "joy8"},
{joy_xi9, "joy9"}, {joy_xi10, "joy10"}, {joy_xi11, "joy11"}, {joy_xi12, "joy12"},
{joy_xi13, "joy13"}, {joy_xi14, "joy14"}, {joy_xi15, "joy15"}, {joy_xi16, "joy16"},
};
static bool ButtonSpriteLoaded = false;
static BYTE ButtonSpriteSpacing[256] = {0};
static BYTE ButtonSpriteSWR[256*256] = {0};
static int compareMap(const void *a, const void *b) {
return strcmp(((const MAP *)a)->name, ((const MAP *)b)->name);
}
static void sortMaps() {
static bool once = false;
if( !once ) {
qsort(btnMap, ARRAY_SIZE(btnMap), sizeof(MAP), compareMap);
qsort(joyMapDI, ARRAY_SIZE(joyMapDI), sizeof(MAP), compareMap);
qsort(joyMapPS, ARRAY_SIZE(joyMapPS), sizeof(MAP), compareMap);
qsort(joyMapXI, ARRAY_SIZE(joyMapXI), sizeof(MAP), compareMap);
once = true;
}
}
static int searchMap(const char *name, MAP *mapArray, DWORD mapCount) {
if( !name || !*name ) return -1;
MAP keyItem = {0, name};
MAP *foundItem = (MAP *)bsearch(&keyItem, mapArray, mapCount, sizeof(MAP), compareMap);
return foundItem ? foundItem->id : -1;
}
static bool ParseSpriteInfo(json_value *root, int id) {
if( root == NULL || root->type != json_object || id < 0 || id >= button_sprites_number ) {
return false;
}
PHD_SPRITE *info = &PhdSpriteInfo[BTN_SPR_IDX + id];
BYTE u = GetJsonIntegerFieldValue(root, "u", 0);
BYTE v = GetJsonIntegerFieldValue(root, "v", 0);
int x = GetJsonIntegerFieldValue(root, "x", 0);
int y = GetJsonIntegerFieldValue(root, "y", 0);
int width = GetJsonIntegerFieldValue(root, "width", 0);
int height = GetJsonIntegerFieldValue(root, "height", 0);
ButtonSpriteSpacing[id] = GetJsonIntegerFieldValue(root, "spacing", 0);
info->offset = (v << 8) | u;
info->width = ABS(width)*256;
info->height = ABS(height)*256;
info->x1 = x;
info->y1 = y;
info->x2 = x + width;
info->y2 = y + height;
return true;
}
static bool ParseButtonSprites(json_value *root) {
if( root == NULL || root->type != json_object ) {
return false;
}
sortMaps();
for( DWORD i = 0; i < root->u.object.length; ++i ) {
if( root->u.object.values[i].value->type != json_object ) continue;
int id = searchMap(root->u.object.values[i].name, btnMap, ARRAY_SIZE(btnMap));
ParseSpriteInfo(root->u.object.values[i].value, id);
}
return true;
}
static BYTE FindPaletteEntry(RGB888 *palette, int red, int green, int blue) {
UINT16 result = 0;
int diffMin = INT_MAX;
// skip index 0 as it is reserved as semitransparent
for( int i=1; i<256; ++i ) {
int diffRed = red - GamePalette8[i].red;
int diffGreen = green - GamePalette8[i].green;
int diffBlue = blue - GamePalette8[i].blue;
int diffTotal = SQR(diffRed) + SQR(diffGreen) + SQR(diffBlue);
if( diffTotal < diffMin ) {
diffMin = diffTotal;
result = i;
}
}
return result;
}
static void AdaptToPalette(void *srcData, int width, int height, int srcPitch, RGB888 *srcPalette, void *dstData, int dstPitch, RGB888 *dstPalette) {
int i, j;
BYTE *src, *dst;
BYTE bufPalette[256] = {0};
// skip index 0 as it is reserved as semitransparent
for( i=1; i<256; ++i ) {
bufPalette[i] = FindPaletteEntry(dstPalette, srcPalette[i].red, srcPalette[i].green, srcPalette[i].blue);
}
src = (BYTE *)srcData;
dst = (BYTE *)dstData;
for( i=0; i= 0x900)
if( SavedAppSettings.RenderMode == RM_Hardware && PathFileExists("textures/buttons.png") ) {
pageIndex = AddExternalTexture("textures/buttons.png", true);
if( pageIndex >= 0 ) {
HWR_TexturePageIndexes[HwrTexturePagesCount] = pageIndex;
HWR_PageHandles[HwrTexturePagesCount] = GetTexturePageHandle(pageIndex);
pageIndex = HwrTexturePagesCount++;
if( isExternal ) *isExternal = true;
return pageIndex;
}
}
if( isExternal ) *isExternal = false;
#endif // (DIRECT3D_VERSION >= 0x900)
LPCBYTE pcxData = (LPCBYTE)GetResourceData("BUTTONS.PCX", &pcxSize);
if( !pcxData || !pcxSize || GetPcxResolution(pcxData, pcxSize, &width, &height)
|| width != height || (SavedAppSettings.RenderMode != RM_Hardware && width != 256) )
{
return -1;
}
RGB888 bmpPal[256] = {{0,0,0}};
BYTE *bitmap = (BYTE *)malloc(width * height);
if( bitmap != NULL ) {
if( DecompPCX(pcxData, pcxSize, bitmap, bmpPal) ) {
if( SavedAppSettings.RenderMode != RM_Hardware || TextureFormat.bpp < 16 ) {
AdaptToPalette(bitmap, width, height, width, bmpPal, bitmap, width, GamePalette8);
memcpy(bmpPal, GamePalette8, sizeof(bmpPal));
}
pageIndex = MakeCustomTexture(0, 0, width, height, width, width, 8, bitmap, bmpPal, PaletteIndex, ButtonSpriteSWR, true);
if( pageIndex >= 0 && SavedAppSettings.RenderMode == RM_Hardware ) {
HWR_TexturePageIndexes[HwrTexturePagesCount] = pageIndex;
HWR_PageHandles[HwrTexturePagesCount] = GetTexturePageHandle(pageIndex);
pageIndex = HwrTexturePagesCount++;
}
}
free(bitmap);
}
return pageIndex;
}
bool LoadButtonSprites() {
ButtonSpriteLoaded = false;
memset(&PhdSpriteInfo[BTN_SPR_IDX], 0, sizeof(PHD_SPRITE) * button_sprites_number);
bool isExternalTexture = false;
int pageIndex = LoadButtonSpriteTexturePage(&isExternalTexture);
if( pageIndex >= 0 ) {
for( int i = 0; i < button_sprites_number; ++i ) {
PhdSpriteInfo[BTN_SPR_IDX + i].texPage = pageIndex;
}
DWORD jsonSize = 0;
#if (DIRECT3D_VERSION >= 0x900)
bool isExternalJson = false;
LPVOID jsonData = NULL;
if( isExternalTexture && PathFileExists("textures/buttons.json") ) {
DWORD bytesRead = 0;
HANDLE hFile = CreateFile("textures/buttons.json", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN|FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile != INVALID_HANDLE_VALUE ) {
isExternalJson = true;
jsonSize = GetFileSize(hFile, NULL);
jsonData = malloc(jsonSize);
ReadFile(hFile, jsonData, jsonSize, &bytesRead, NULL);
CloseHandle(hFile);
}
}
if( !isExternalJson ) {
jsonData = (LPVOID)GetResourceData("BUTTONS.JSON", &jsonSize);
}
#else // (DIRECT3D_VERSION >= 0x900)
LPCVOID jsonData = GetResourceData("BUTTONS.JSON", &jsonSize);
#endif // (DIRECT3D_VERSION >= 0x900)
if( jsonData && jsonSize ) {
json_value* json = json_parse((const json_char *)jsonData, jsonSize);
if( json != NULL ) {
ButtonSpriteLoaded = ParseButtonSprites(json);
json_value_free(json);
}
}
#if (DIRECT3D_VERSION >= 0x900)
if( isExternalJson && jsonData ) {
free(jsonData);
}
#endif // (DIRECT3D_VERSION >= 0x900)
}
return ButtonSpriteLoaded;
}
bool GetTextSpriteByName(const char *name, int nameLen, DWORD *sprite, int *spacing) {
int id = -1;
char mapName[64] = {0};
if( !ButtonSpriteLoaded || name == NULL || nameLen <= 0 || (DWORD)nameLen >= sizeof(mapName) ) {
return false;
}
memcpy(mapName, name, nameLen);
DWORD buttonStyle = JoystickButtonStyle;
#ifdef FEATURE_INPUT_IMPROVED
if( buttonStyle == 0 ) {
switch( GetJoystickType() ) {
case JT_XINPUT:
buttonStyle = 3;
break;
case JT_PLAYSTATION:
buttonStyle = 2;
break;
case JT_DIRECTINPUT:
case JT_NONE:
buttonStyle = 1;
break;
}
}
#endif // FEATURE_INPUT_IMPROVED
switch( buttonStyle ) {
case 0:
case 1:
id = searchMap(mapName, joyMapDI, ARRAY_SIZE(joyMapDI));
break;
case 2:
id = searchMap(mapName, joyMapPS, ARRAY_SIZE(joyMapPS));
break;
case 3:
id = searchMap(mapName, joyMapXI, ARRAY_SIZE(joyMapXI));
break;
}
if( id < 0 || id >= button_sprites_number ) {
id = searchMap(mapName, btnMap, ARRAY_SIZE(btnMap));
}
if( id < 0 || id >= button_sprites_number ) {
return false;
}
if( sprite ) *sprite = BTN_SPR_IDX + id;
if( spacing ) *spacing = ButtonSpriteSpacing[id];
return true;
}
#endif // FEATURE_HUD_IMPROVED
// This prevents texture bleeding instead of UV adjustment
static int FillEdgePadding(DWORD width, DWORD height, DWORD side, BYTE *bitmap, DWORD bpp) {
if( !width || !height || width > side || height > side || bitmap == NULL ) {
return -1;
}
switch( bpp ) {
case 8 :
case 16 :
case 32 :
break;
default :
return -1;
}
DWORD i;
DWORD padRight = side - width;
DWORD padBottom = side - height;
if( padRight > 0 ) {
switch( bpp ) {
case 8 : {
BYTE *p = (BYTE *)bitmap;
for( i = 0; i < height ; ++i ) {
p += width;
p[0] = p[-1];
p += padRight;
}
break;
}
case 16 : {
UINT16 *p = (UINT16 *)bitmap;
for( i = 0; i < height ; ++i ) {
p += width;
p[0] = p[-1];
p += padRight;
}
break;
}
case 32 : {
DWORD *p = (DWORD *)bitmap;
for( i = 0; i < height ; ++i ) {
p += width;
p[0] = p[-1];
p += padRight;
}
break;
}
default :
break;
}
}
if( padBottom > 0 ) {
DWORD pitch = (width + padRight?1:0) * (bpp/8);
BYTE *p = bitmap + height * pitch;
memcpy(p, p - pitch, pitch);
p += pitch;
}
return 0;
}
int MakeCustomTexture(DWORD x, DWORD y, DWORD width, DWORD height, DWORD pitch, DWORD side, DWORD bpp, BYTE *bitmap, RGB888 *bmpPal, int hwrPal, BYTE *swrBuf, bool keyColor) {
int pageIndex = -1;
if( bpp == 16 ) {
if( SavedAppSettings.RenderMode != RM_Hardware || TextureFormat.bpp < 16 ) { // texture cannot be indexed in this case
return -1;
}
UINT16 *tmpBmp = (UINT16 *)calloc(2, SQR(side));
UINT16 *bmpDst = tmpBmp;
UINT16 *bmpSrc = (UINT16 *)bitmap + x + y * pitch;
for( DWORD j = 0; j < height; ++j ) {
memcpy(bmpDst, bmpSrc, sizeof(UINT16) * width);
bmpSrc += pitch;
bmpDst += side;
}
FillEdgePadding(width, height, side, (BYTE *)tmpBmp, bpp);
pageIndex = AddTexturePage16(side, side, (BYTE *)tmpBmp);
free(tmpBmp);
#if (DIRECT3D_VERSION >= 0x900)
} else if( bpp == 32 ) {
if( SavedAppSettings.RenderMode != RM_Hardware || TextureFormat.bpp < 16 ) { // texture cannot be indexed in this case
return -1;
}
DWORD *tmpBmp = (DWORD *)calloc(4, SQR(side));
DWORD *bmpDst = tmpBmp;
DWORD *bmpSrc = (DWORD *)bitmap + x + y * pitch;
for( DWORD j = 0; j < height; ++j ) {
memcpy(bmpDst, bmpSrc, sizeof(DWORD) * width);
bmpSrc += pitch;
bmpDst += side;
}
FillEdgePadding(width, height, side, (BYTE *)tmpBmp, bpp);
pageIndex = AddTexturePage32(side, side, (BYTE *)tmpBmp, false);
free(tmpBmp);
#endif // (DIRECT3D_VERSION >= 0x900)
} else if( SavedAppSettings.RenderMode == RM_Hardware && TextureFormat.bpp >= 16 ) {
UINT16 *tmpBmp = (UINT16 *)calloc(2, SQR(side));
UINT16 *bmpDst = tmpBmp;
BYTE *bmpSrc = bitmap + x + y * pitch;
// Translating bitmap data from 8 bit bitmap to 16 bit bitmap
for( DWORD j = 0; j < height; ++j ) {
for( DWORD i = 0; i < width; ++i ) {
if( !keyColor || bmpSrc[i] ) {
RGB888 *color = &bmpPal[bmpSrc[i]]; // get RGB color from palette
bmpDst[i] = (1 << 15) // convert RGB to 16 bit
| (((UINT16)color->red >> 3) << 10)
| (((UINT16)color->green >> 3) << 5)
| (((UINT16)color->blue >> 3));
} else {
bmpDst[i] = 0;
}
}
bmpSrc += pitch;
bmpDst += side;
}
FillEdgePadding(width, height, side, (BYTE *)tmpBmp, 16);
pageIndex = AddTexturePage16(side, side, (BYTE *)tmpBmp);
free(tmpBmp);
} else if( SavedAppSettings.RenderMode == RM_Hardware ) {
BYTE *tmpBmp = (BYTE *)calloc(1, SQR(side));
UT_MemBlt(tmpBmp, 0, 0, width, height, side, bitmap, x, y, pitch);
FillEdgePadding(width, height, side, tmpBmp, 8);
pageIndex = AddTexturePage8(side, side, tmpBmp, hwrPal);
free(tmpBmp);
} else if( swrBuf != NULL && width == 256 && height == 256 && side == 256 ) {
for( DWORD i=0; i= 0x900)
#define TEXPAGE_CONFIG_NAME "textures/texpages/config.json"
typedef struct {
bool isLoaded;
bool isLegacyColors;
double adjustment;
#ifdef FEATURE_HUD_IMPROVED
struct {
int spacing;
int xOffset;
int yOffset;
double xStretch;
double yStretch;
} glyphs[110];
#endif // FEATURE_HUD_IMPROVED
} TEXPAGES_CONFIG;
static TEXPAGES_CONFIG TexPagesConfig;
bool IsTexPagesConfigLoaded() {
return TexPagesConfig.isLoaded;
}
bool IsTexPagesLegacyColors() {
return TexPagesConfig.isLegacyColors;
}
double GetTexPagesAdjustment() {
return TexPagesConfig.adjustment;
}
#ifdef FEATURE_HUD_IMPROVED
int GetTexPagesGlyphSpacing(int id) {
if( id < 0 || id >= (int)ARRAY_SIZE(TexPagesConfig.glyphs)
|| SavedAppSettings.RenderMode == RM_Software )
{
return 0;
}
return TexPagesConfig.glyphs[id].spacing;
}
int GetTexPagesGlyphXOffset(int id) {
if( id < 0 || id >= (int)ARRAY_SIZE(TexPagesConfig.glyphs)
|| SavedAppSettings.RenderMode == RM_Software )
{
return 0;
}
return TexPagesConfig.glyphs[id].xOffset;
}
int GetTexPagesGlyphYOffset(int id) {
if( id < 0 || id >= (int)ARRAY_SIZE(TexPagesConfig.glyphs)
|| SavedAppSettings.RenderMode == RM_Software )
{
return 0;
}
return TexPagesConfig.glyphs[id].yOffset;
}
double GetTexPagesGlyphXStretch(int id) {
if( id < 0 || id >= (int)ARRAY_SIZE(TexPagesConfig.glyphs)
|| SavedAppSettings.RenderMode == RM_Software
|| TexPagesConfig.glyphs[id].xStretch <= 0.0 )
{
return 1.0;
}
return TexPagesConfig.glyphs[id].xStretch;
}
double GetTexPagesGlyphYStretch(int id) {
if( id < 0 || id >= (int)ARRAY_SIZE(TexPagesConfig.glyphs)
|| SavedAppSettings.RenderMode == RM_Software
|| TexPagesConfig.glyphs[id].yStretch <= 0.0 )
{
return 1.0;
}
return TexPagesConfig.glyphs[id].yStretch;
}
#endif // FEATURE_HUD_IMPROVED
static bool ParseLevelTexPagesConfiguration(json_value *root) {
if( root == NULL || root->type != json_object ) {
return false;
}
json_value* field = NULL;
field = GetJsonField(root, json_boolean, "legacy_colors", NULL);
if( field ) {
TexPagesConfig.isLegacyColors = field->u.boolean;
}
field = GetJsonField(root, json_double, "uv_adjust", NULL);
if( field ) {
TexPagesConfig.adjustment = field->u.dbl;
}
#ifdef FEATURE_HUD_IMPROVED
json_value* glyphs = GetJsonField(root, json_array, "glyphs", NULL);
if( glyphs ) {
for( DWORD i = 0; i < glyphs->u.array.length; ++i ) {
json_value *glyph = glyphs->u.array.values[i];
int id = GetJsonIntegerFieldValue(glyph, "id", -1);
if( id < 0 || id >= (int)ARRAY_SIZE(TexPagesConfig.glyphs) ) continue;
TexPagesConfig.glyphs[id].spacing = GetJsonIntegerFieldValue(glyph, "spacing", 0);
TexPagesConfig.glyphs[id].xOffset = GetJsonIntegerFieldValue(glyph, "x_offset", 0);
TexPagesConfig.glyphs[id].yOffset = GetJsonIntegerFieldValue(glyph, "y_offset", 0);
TexPagesConfig.glyphs[id].xStretch = GetJsonFloatFieldValue(glyph, "x_stretch", 1.0);
TexPagesConfig.glyphs[id].yStretch = GetJsonFloatFieldValue(glyph, "y_stretch", 1.0);
}
}
#endif // FEATURE_HUD_IMPROVED
return true;
}
static bool ParseTexPagesConfiguration(char *levelName, json_value *root) {
if( root == NULL || root->type != json_object ) {
return false;
}
// parsing default configs
ParseLevelTexPagesConfiguration(GetJsonField(root, json_object, "default", NULL));
// parsing level specific configs
json_value* levels = GetJsonField(root, json_array, "levels", NULL);
if( levels ) ParseLevelTexPagesConfiguration(GetJsonObjectByStringField(levels, "filename", levelName, false, NULL));
return true;
}
void UnloadTexPagesConfiguration() {
memset(&TexPagesConfig, 0, sizeof(TexPagesConfig));
}
bool LoadTexPagesConfiguration(LPCTSTR levelFilePath) {
UnloadTexPagesConfiguration();
if( !PathFileExists(TEXPAGE_CONFIG_NAME) ) {
return false;
}
char levelName[256] = {0};
strncpy(levelName, PathFindFileName(levelFilePath), sizeof(levelName)-1);
char *ext = PathFindExtension(levelName);
if( ext != NULL ) *ext = 0;
DWORD bytesRead = 0;
HANDLE hFile = CreateFile(TEXPAGE_CONFIG_NAME, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN|FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE ) {
return false;
}
DWORD cfgSize = GetFileSize(hFile, NULL);
void *cfgData = malloc(cfgSize);
ReadFile(hFile, cfgData, cfgSize, &bytesRead, NULL);
CloseHandle(hFile);
json_value* json = json_parse((const json_char *)cfgData, cfgSize);
if( json != NULL ) {
TexPagesConfig.isLoaded = ParseTexPagesConfiguration(levelName, json);
}
json_value_free(json);
free(cfgData);
return TexPagesConfig.isLoaded;
}
#endif // (DIRECT3D_VERSION >= 0x900)
================================================
FILE: modding/texture_utils.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef TEXTURE_UTILS_H_INCLUDED
#define TEXTURE_UTILS_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#ifdef FEATURE_HUD_IMPROVED
#define HUD_SPRITE_RESERVED (256)
bool LoadButtonSprites();
bool GetTextSpriteByName(const char *name, int nameLen, DWORD *sprite, int *spacing);
#endif // FEATURE_HUD_IMPROVED
int MakeCustomTexture(DWORD x, DWORD y, DWORD width, DWORD height, DWORD pitch, DWORD side, DWORD bpp, BYTE *bitmap, RGB888 *bmpPal, int hwrPal, BYTE *swrBuf, bool keyColor);
#if (DIRECT3D_VERSION >= 0x900)
bool IsTexPagesConfigLoaded();
bool IsTexPagesLegacyColors();
double GetTexPagesAdjustment();
#ifdef FEATURE_HUD_IMPROVED
int GetTexPagesGlyphSpacing(int id);
int GetTexPagesGlyphXOffset(int id);
int GetTexPagesGlyphYOffset(int id);
double GetTexPagesGlyphXStretch(int id);
double GetTexPagesGlyphYStretch(int id);
#endif // FEATURE_HUD_IMPROVED
void UnloadTexPagesConfiguration();
bool LoadTexPagesConfiguration(LPCTSTR levelFilePath);
#endif // (DIRECT3D_VERSION >= 0x900)
#endif // TEXTURE_UTILS_H_INCLUDED
================================================
FILE: modding/xinput_ex.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "modding/xinput_ex.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
// Imports from xinput*.dll
static HMODULE hXInput = NULL;
static void (WINAPI *_XInputEnable)(WINBOOL) = NULL;
static DWORD (WINAPI *_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*) = NULL;
static DWORD (WINAPI *_XInputSetState)(DWORD, XINPUT_VIBRATION*) = NULL;
static DWORD (WINAPI *_XInputGetState)(DWORD, XINPUT_STATE*) = NULL;
static void XInputLibUnlink() {
_XInputEnable = NULL;
_XInputSetState = NULL;
_XInputGetState = NULL;
_XInputGetCapabilities = NULL;
if( hXInput != NULL ) {
FreeLibrary(hXInput);
hXInput = NULL;
}
}
static bool XInputLibLink(LPCSTR lpLibFileName, bool advanced) {
HMODULE hXInput = LoadLibrary(lpLibFileName);
if( hXInput == NULL ) {XInputLibUnlink(); return false;}
// XInputEnable is optional, so don't check if it's NULL
*(FARPROC *)&_XInputEnable = GetProcAddress(hXInput, "XInputEnable");
*(FARPROC *)&_XInputGetCapabilities = GetProcAddress(hXInput, "XInputGetCapabilities");
if( _XInputGetCapabilities == NULL ) {XInputLibUnlink(); return false;}
*(FARPROC *)&_XInputSetState = GetProcAddress(hXInput, "XInputSetState");
if( _XInputSetState == NULL ) {XInputLibUnlink(); return false;}
if( advanced ) {
// The hack to get XInput Guide button state, works for some xinput*.dll versions
*(FARPROC *)&_XInputGetState = GetProcAddress(hXInput, MAKEINTRESOURCE(100));
}
if( _XInputGetState == NULL ) {
*(FARPROC *)&_XInputGetState = GetProcAddress(hXInput, "XInputGetState");
}
if( _XInputGetState == NULL ) {XInputLibUnlink(); return false;}
return true;
}
static bool XInputLibInit() {
static const struct {
LPCSTR name; bool advanced;
} libs[] = {
{"xinput1_4.dll", true},
{"xinput1_3.dll", true},
{"xinput9_1_0.dll", false},
{"xinput1_2.dll", false},
{"xinput1_1.dll", false},
};
static bool failed = false;
if( hXInput != NULL ) {
return true;
} else if( failed ) {
return false;
}
for( DWORD i=0; i
#include
BOOL IsXInputDevice(DWORD dwVendorId, DWORD dwProductId) {
IWbemLocator* pIWbemLocator = NULL;
IEnumWbemClassObject* pEnumDevices = NULL;
IWbemClassObject* pDevices[20] = {0};
IWbemServices* pIWbemServices = NULL;
BSTR bstrNamespace = NULL;
BSTR bstrDeviceID = NULL;
BSTR bstrClassName = NULL;
DWORD uReturned = 0;
bool bIsXinputDevice = false;
UINT iDevice = 0;
VARIANT var;
HRESULT hr;
// CoInit if needed
hr = CoInitialize(NULL);
bool bCleanupCOM = SUCCEEDED(hr);
// Create WMI
hr = CoCreateInstance(__uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IWbemLocator), (LPVOID*) &pIWbemLocator);
if( FAILED(hr) || pIWbemLocator == NULL ) goto LCleanup;
bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2");
if( bstrNamespace == NULL ) goto LCleanup;
bstrClassName = SysAllocString(L"Win32_PNPEntity");
if( bstrClassName == NULL ) goto LCleanup;
bstrDeviceID = SysAllocString(L"DeviceID");
if( bstrDeviceID == NULL ) goto LCleanup;
// Connect to WMI
hr = pIWbemLocator->ConnectServer(bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices);
if( FAILED(hr) || pIWbemServices == NULL ) goto LCleanup;
// Switch security level to IMPERSONATE.
CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, NULL, &pEnumDevices);
if( FAILED(hr) || pEnumDevices == NULL ) goto LCleanup;
// Loop over all devices
for( ;; ) {
// Get 20 at a time
hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned);
if( FAILED(hr) ) goto LCleanup;
if( uReturned == 0 ) break;
for( iDevice=0; iDeviceGet(bstrDeviceID, 0L, &var, NULL, NULL);
if( SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL ) {
// Check if the device ID contains "IG_". If it does, then it's an XInput device
if( wcsstr(var.bstrVal, L"IG_") ) {
// If it does, then get the VID/PID from var.bstrVal
DWORD dwPid = 0, dwVid = 0;
WCHAR* strVid = wcsstr( var.bstrVal, L"VID_" );
if( strVid && swscanf( strVid, L"VID_%4X", &dwVid ) != 1 )
dwVid = 0;
WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" );
if( strPid && swscanf( strPid, L"PID_%4X", &dwPid ) != 1 )
dwPid = 0;
// Compare the VID/PID to the DInput device
if( dwVendorId == dwVid && dwProductId == dwPid )
{
bIsXinputDevice = true;
goto LCleanup;
}
}
}
if( pDevices[iDevice] ) {
pDevices[iDevice]->Release();
pDevices[iDevice] = NULL;
}
}
}
LCleanup:
if( bstrNamespace ) SysFreeString(bstrNamespace);
if( bstrDeviceID ) SysFreeString(bstrDeviceID);
if( bstrClassName ) SysFreeString(bstrClassName);
for( iDevice=0; iDevice<20; iDevice++ ) {
if( pDevices[iDevice] ) pDevices[iDevice]->Release();
}
if( pEnumDevices ) pEnumDevices->Release();
if( pIWbemLocator ) pIWbemLocator->Release();
if( pIWbemServices ) pIWbemServices->Release();
if( bCleanupCOM ) CoUninitialize();
return bIsXinputDevice;
}
#endif // FEATURE_INPUT_IMPROVED
================================================
FILE: modding/xinput_ex.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef XINPUT_EX_H_INCLUDED
#define XINPUT_EX_H_INCLUDED
#include "global/types.h"
#ifdef FEATURE_INPUT_IMPROVED
// all essential definitions are inside XInput.h, but no static lib linked here
#include
#ifndef XINPUT_GAMEPAD_GUIDE
#define XINPUT_GAMEPAD_GUIDE (0x400)
#endif // XINPUT_GAMEPAD_GUIDE
#define XINPUT_DPAD(x) (CHK_ALL((x), XINPUT_GAMEPAD_DPAD_UP|XINPUT_GAMEPAD_DPAD_DOWN|XINPUT_GAMEPAD_DPAD_LEFT|XINPUT_GAMEPAD_DPAD_RIGHT))
BOOL IsXInputDevice(DWORD dwVendorId, DWORD dwProductId);
#endif // FEATURE_INPUT_IMPROVED
#endif // XINPUT_EX_H_INCLUDED
================================================
FILE: specific/background.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/background.h"
#include "specific/hwr.h"
#include "specific/init.h"
#include "specific/texture.h"
#include "specific/utils.h"
#include "global/vars.h"
#include
#ifdef FEATURE_BACKGROUND_IMPROVED
#include "modding/background_new.h"
extern int PatternTexPage;
DWORD InvBackgroundMode = 2;
DWORD StatsBackgroundMode = 0;
#endif // FEATURE_BACKGROUND_IMPROVED
void __cdecl BGND_Make640x480(BYTE *bitmap, RGB888 *palette) {
// NOTE: 8 bit bitmap may be converted to 16 bit right in the tmpBuffer
// so we need to allocate memory for 16 bit bitmap anyway
DWORD tmpBufSize = 256*256*2;
BYTE *tmpBuffer = (BYTE *)game_malloc(tmpBufSize, GBUF_TempAlloc);
BGND_PaletteIndex = (TextureFormat.bpp < 16) ? CreateTexturePalette(palette) : -1;
UT_MemBlt(tmpBuffer, 0, 0, 256, 256, 256, bitmap, 0, 0, 640);
BGND_AddTexture(0, tmpBuffer, BGND_PaletteIndex, palette);
UT_MemBlt(tmpBuffer, 0, 0, 256, 256, 256, bitmap, 256, 0, 640);
BGND_AddTexture(1, tmpBuffer, BGND_PaletteIndex, palette);
UT_MemBlt(tmpBuffer, 0, 0, 128, 256, 256, bitmap, 512, 0, 640);
UT_MemBlt(tmpBuffer, 128, 0, 128, 224, 256, bitmap, 512, 256, 640);
BGND_AddTexture(2, tmpBuffer, BGND_PaletteIndex, palette);
UT_MemBlt(tmpBuffer, 0, 0, 256, 224, 256, bitmap, 0, 256, 640);
BGND_AddTexture(3, tmpBuffer, BGND_PaletteIndex, palette);
UT_MemBlt(tmpBuffer, 0, 0, 256, 224, 256, bitmap, 256, 256, 640);
BGND_AddTexture(4, tmpBuffer, BGND_PaletteIndex, palette);
game_free(tmpBufSize);
BGND_GetPageHandles();
BGND_PictureIsReady = true;
}
int __cdecl BGND_AddTexture(int tileIndex, BYTE *bitmap, int palIndex, RGB888 *bmpPal) {
int pageIndex;
BYTE *bmpSrc;
UINT16 *bmpDst;
// If destination palette is absent we convert 8 bit to 16 bit right in the buffer
if( palIndex < 0 ) {
bmpDst = (UINT16 *)&bitmap[256*256*2]; // (16 bit bitmap end)
bmpSrc = (BYTE *)&bitmap[256*256*1]; // (8 bit bitmap end / 16 bit bitmap center)
// Translating bitmap data from 8 bit bitmap end to 16 bit bitmap end
for( int i=256*256; i>0; --i ) {
RGB888 *color = &bmpPal[*(--bmpSrc)]; // get RGB color from palette
*(--bmpDst) = (1 << 15) // convert RGB to 16 bit
| (((UINT16)color->red >> 3) << 10)
| (((UINT16)color->green >> 3) << 5)
| (((UINT16)color->blue >> 3));
}
// Now both bmpSrc and bmpDst pointers = bitmap pointer
pageIndex = AddTexturePage16(256, 256, bitmap);
} else {
pageIndex = AddTexturePage8(256, 256, bitmap, palIndex);
}
BGND_TexturePageIndexes[tileIndex] = ( pageIndex >= 0 ) ? pageIndex : -1;
return pageIndex;
}
void __cdecl BGND_GetPageHandles() {
for( DWORD i=0; i= 0 ) {
tu = tv = 0;
twidth = theight = 256;
texSource = HWR_PageHandles[PatternTexPage];
} else {
meshPtr = MeshPtr[Objects[ID_INV_BACKGROUND].meshIndex];
meshPtr += 3+2; // skip mesh coords (3*INT16) and radius (1*INT32)
numVertices = *(meshPtr++);
meshPtr += numVertices*3; // skip vertices (each one is 3xINT16)
numNormals = *(meshPtr++);
if( numNormals >= 0 ) // negative num means lights instead of normals
meshPtr += numNormals*3; // skip normals (each is 3xINT16)
else
meshPtr -= numNormals; // skip lights (each one is INT16)
numQuads = *(meshPtr++);
if( numQuads < 1 ) {
BGND_DrawInGameBlack();
return;
}
meshPtr += 4; // skip 4 vertice indices of 1st textured quad (each one is INT16)
textureIndex = *(meshPtr++); // get texture index of 1st textured quad.
textureInfo = &PhdTextureInfo[textureIndex];
texSource = HWR_PageHandles[textureInfo->tpage];
tu = textureInfo->uv[0].u / PHD_HALF;
tv = textureInfo->uv[0].v / PHD_HALF;
twidth = textureInfo->uv[2].u / PHD_HALF - tu + 1;
theight = textureInfo->uv[2].v / PHD_HALF - tv + 1;
}
HWR_EnableZBuffer(false, false);
if( PatternTexPage >= 0 ) {
#if (DIRECT3D_VERSION >= 0x900)
D3DDev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
D3DDev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
#else // (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRENDERSTATE_TEXTUREADDRESS, D3DTADDRESS_WRAP);
#endif // (DIRECT3D_VERSION >= 0x900)
}
if( bgndMode == 2 ) {
static const int shortWaveStep = -0x0267; // minus 3.92 degrees
static const int longWaveStep = -0x0200; // minus 2.81 degrees
static UINT16 deformWavePhase = 0x0000; // 0 degrees
static UINT16 shortWavePhase = 0x4000; // 90 degrees
static UINT16 longWavePhase = 0xA000; // 225 degrees
PSX_Background(texSource, tu, tv, twidth, theight, 3, 10, deformWavePhase, shortWavePhase, longWavePhase);
deformWavePhase += shortWaveStep;
shortWavePhase += shortWaveStep;
longWavePhase += longWaveStep;
goto CLEANUP;
}
y_count = 6;
x_count = MulDiv(y_count, PhdWinWidth, PhdWinHeight);
#else // !FEATURE_BACKGROUND_IMPROVED
meshPtr = MeshPtr[Objects[ID_INV_BACKGROUND].meshIndex];
meshPtr += 3+2; // skip mesh coords (3*INT16) and radius (1*INT32)
numVertices = *(meshPtr++);
meshPtr += numVertices*3; // skip vertices (each one is 3xINT16)
numNormals = *(meshPtr++);
if( numNormals >= 0 ) // negative num means lights instead of normals
meshPtr += numNormals*3; // skip normals (each is 3xINT16)
else // NOTE: additional check. Absent in the original code
meshPtr -= numNormals; // skip lights (each one is INT16)
numQuads = *(meshPtr++);
if( numQuads < 1 ) {
// NOTE: additional check. Absent in the original code
BGND_DrawInGameBlack();
return;
}
meshPtr += 4; // skip 4 vertice indices of 1st textured quad (each one is INT16)
textureIndex = *(meshPtr++); // get texture index of 1st textured quad.
textureInfo = &PhdTextureInfo[textureIndex];
texSource = HWR_PageHandles[textureInfo->tpage];
tu = textureInfo->uv[0].u / PHD_HALF;
tv = textureInfo->uv[0].v / PHD_HALF;
twidth = textureInfo->uv[2].u / PHD_HALF - tu + 1;
theight = textureInfo->uv[2].v / PHD_HALF - tv + 1;
HWR_EnableZBuffer(false, false);
x_count = 8;
y_count = 6;
#endif // FEATURE_BACKGROUND_IMPROVED
y_current = 0;
for( int i=0; i= 0 ) {
#if (DIRECT3D_VERSION >= 0x900)
D3DDev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
D3DDev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
#else // (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRENDERSTATE_TEXTUREADDRESS, D3DTADDRESS_CLAMP);
#endif // (DIRECT3D_VERSION >= 0x900)
}
#endif // FEATURE_BACKGROUND_IMPROVED
HWR_EnableZBuffer(true, true);
}
void __cdecl DrawTextureTile(int sx, int sy, int width, int height, HWR_TEXHANDLE texSource,
int tu, int tv, int t_width, int t_height,
D3DCOLOR color0, D3DCOLOR color1, D3DCOLOR color2, D3DCOLOR color3)
{
float sx0, sy0, sx1, sy1;
float tu0, tv0, tu1, tv1;
double uvAdjust;
D3DTLVERTEX vertex[4];
sx0 = (double)sx;
sy0 = (double)sy;
sx1 = (double)(sx + width);
sy1 = (double)(sy + height);
#ifdef FEATURE_BACKGROUND_IMPROVED
tu0 = (double)tu / 256.0;
tv0 = (double)tv / 256.0;
tu1 = (double)(tu + t_width) / 256.0;
tv1 = (double)(tv + t_height) / 256.0;
if( PatternTexPage < 0 ) {
uvAdjust = (double)UvAdd / (double)(256 * GetTextureSideByHandle(texSource));
CLAMPL(uvAdjust, 1.0/double(PHD_ONE));
tu0 += uvAdjust;
tv0 += uvAdjust;
tu1 -= uvAdjust;
tv1 -= uvAdjust;
}
#else // FEATURE_BACKGROUND_IMPROVED
// NOTE: page side is not counted in the original game, but we need it for HD textures
uvAdjust = (double)UvAdd / (double)(256 * GetTextureSideByHandle(texSource));
CLAMPL(uvAdjust, 1.0/double(PHD_ONE));
tu0 = (double)tu / 256.0 + uvAdjust;
tv0 = (double)tv / 256.0 + uvAdjust;
tu1 = (double)(tu + t_width) / 256.0 - uvAdjust;
tv1 = (double)(tv + t_height) / 256.0 - uvAdjust;
#endif // FEATURE_BACKGROUND_IMPROVED
vertex[0].sx = sx0;
vertex[0].sy = sy0;
vertex[0].color = color0;
vertex[0].tu = tu0;
vertex[0].tv = tv0;
vertex[1].sx = sx1;
vertex[1].sy = sy0;
vertex[1].color = color1;
vertex[1].tu = tu1;
vertex[1].tv = tv0;
vertex[2].sx = sx0;
vertex[2].sy = sy1;
vertex[2].color = color2;
vertex[2].tu = tu0;
vertex[2].tv = tv1;
vertex[3].sx = sx1;
vertex[3].sy = sy1;
vertex[3].color = color3;
vertex[3].tu = tu1;
vertex[3].tv = tv1;
for( int i=0; i<4; ++i ) {
vertex[i].sz = 0.995;
vertex[i].rhw = RhwFactor / FltFarZ;
vertex[i].specular = 0;
}
HWR_TexSource(texSource);
HWR_EnableColorKey(false);
HWR_DrawPrimitive(D3DPT_TRIANGLESTRIP, &vertex, 4, true);
}
D3DCOLOR __cdecl BGND_CenterLighting(int x, int y, int width, int height) {
double xDist, yDist;
int light;
xDist = (double)(x - (width/2)) / (double)width; // xDist range will be: -0.5..0.5
yDist = (double)(y - (height/2)) / (double)height; // yDist range will be: -0.5..0.5
light = 256 - (sqrt(xDist * xDist + yDist * yDist) * 300.0); // light range will be: 44..256
#if defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.LightingMode ) light /= 2;
#endif // defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
// Do light range checks just in case
CLAMP(light, 0, 255);
// combine gray color using light value
return RGBA_MAKE(light, light, light, 0xFFu);
}
void __cdecl BGND_Free() {
for( DWORD i=0; i= 0 ) {
SafeFreeTexturePage(BGND_TexturePageIndexes[i]);
BGND_TexturePageIndexes[i] = -1;
}
BGND_PageHandles[i] = 0;
}
if( BGND_PaletteIndex >= 0 ) {
SafeFreePalette(BGND_PaletteIndex);
BGND_PaletteIndex = -1;
}
}
bool __cdecl BGND_Init() {
BGND_PictureIsReady = false;
BGND_PaletteIndex = -1;
for( DWORD i=0; i .
*/
#ifndef BACKGROUND_H_INCLUDED
#define BACKGROUND_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl BGND_Make640x480(BYTE *bitmap, RGB888 *palette); // 0x00443A40
int __cdecl BGND_AddTexture(int tileIndex, BYTE *bitmap, int palIndex, RGB888 *bmpPal); // 0x00443C00
void __cdecl BGND_GetPageHandles(); // 0x00443CC0
void __cdecl BGND_DrawInGameBlack(); // 0x00443D00
void __cdecl DrawQuad(float sx, float sy, float width, float height, D3DCOLOR color); // 0x00443D60
void __cdecl BGND_DrawInGameBackground(); // 0x00443E40
void __cdecl DrawTextureTile(int sx, int sy, int width, int height, HWR_TEXHANDLE texSource,
int tu, int tv, int t_width, int t_height,
D3DCOLOR color0, D3DCOLOR color1, D3DCOLOR color2, D3DCOLOR color3); // 0x00444060
D3DCOLOR __cdecl BGND_CenterLighting(int x, int y, int width, int height); // 0x004442C0
void __cdecl BGND_Free(); // 0x00444570
bool __cdecl BGND_Init(); // 0x004445C0
#endif // BACKGROUND_H_INCLUDED
================================================
FILE: specific/display.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/display.h"
#include "3dsystem/3d_gen.h"
#include "specific/output.h"
#include "global/vars.h"
void __cdecl IncreaseScreenSize() {
if( ScreenSizer < 1.0 ) {
ScreenSizer += 0.08;
if( ScreenSizer > 1.0 ) {
ScreenSizer = 1.0;
}
GameSizer = ScreenSizer;
setup_screen_size();
}
}
void __cdecl DecreaseScreenSize() {
if( ScreenSizer > 0.44 ) {
ScreenSizer -= 0.08;
if( ScreenSizer < 0.44 ) {
ScreenSizer = 0.44;
}
GameSizer = ScreenSizer;
setup_screen_size();
}
}
void __cdecl setup_screen_size() {
int wwidth, wheight;
int xoff, yoff;
wwidth = (int)((double)GameVidWidth * ScreenSizer);
wheight = (int)((double)GameVidHeight * ScreenSizer);
if( wwidth > GameVidWidth )
wwidth = GameVidWidth;
if( wheight > GameVidHeight )
wheight = GameVidHeight;
xoff = (GameVidWidth - wwidth) / 2;
yoff = (GameVidHeight - wheight) / 2;
#if (DIRECT3D_VERSION >= 0x900)
phd_InitWindow(xoff, yoff, wwidth, wheight, VIEW_NEAR, VIEW_FAR, 80, GameVidWidth, GameVidHeight);
#else // (DIRECT3D_VERSION >= 0x900)
phd_InitWindow(xoff, yoff, wwidth, wheight, VIEW_NEAR, VIEW_FAR, 80, GameVidBufWidth, GameVidBufHeight);
#endif // (DIRECT3D_VERSION >= 0x900)
DumpX = xoff;
DumpY = yoff;
DumpWidth = wwidth;
DumpHeight = wheight;
WinVidNeedToResetBuffers = true;
}
void __cdecl TempVideoAdjust(int hires, double sizer) {
IsVidSizeLock = TRUE;
if( ScreenSizer != sizer ) {
ScreenSizer = sizer;
setup_screen_size();
}
}
void __cdecl TempVideoRemove() {
IsVidSizeLock = FALSE;
if( ScreenSizer != GameSizer ) {
ScreenSizer = GameSizer;
setup_screen_size();
}
}
void __cdecl S_FadeInInventory(BOOL isFade) {
if( InventoryMode != INV_TitleMode )
S_CopyScreenToBuffer();
if( isFade ) {
FadeValue = 0x100000;
FadeLimit = 0x180000;
FadeAdder = +0x08000;
}
}
void __cdecl S_FadeOutInventory(BOOL isFade) {
if( isFade ) {
FadeValue = 0x180000;
FadeLimit = 0x100000;
FadeAdder = -0x08000;
}
}
/*
* Inject function
*/
void Inject_Display() {
INJECT(0x004478C0, IncreaseScreenSize);
INJECT(0x00447930, DecreaseScreenSize);
INJECT(0x004479A0, setup_screen_size);
INJECT(0x00447A40, TempVideoAdjust);
INJECT(0x00447A80, TempVideoRemove);
INJECT(0x00447AC0, S_FadeInInventory);
INJECT(0x00447B00, S_FadeOutInventory);
}
================================================
FILE: specific/display.h
================================================
/*
* Copyright (c) 2017 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef DISPLAY_H_INCLUDED
#define DISPLAY_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl IncreaseScreenSize(); // 0x004478C0
void __cdecl DecreaseScreenSize(); // 0x00447930
void __cdecl setup_screen_size(); // 0x004479A0
void __cdecl TempVideoAdjust(int hires, double sizer); // 0x00447A40
void __cdecl TempVideoRemove(); // 0x00447A80
void __cdecl S_FadeInInventory(BOOL isFade); // 0x00447AC0
void __cdecl S_FadeOutInventory(BOOL isFade); // 0x00447B00
#endif // DISPLAY_H_INCLUDED
================================================
FILE: specific/file.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/file.h"
#include "game/invfunc.h"
#include "game/items.h"
#include "game/setup.h"
#include "specific/frontend.h"
#include "specific/hwr.h"
#include "specific/init.h"
#include "specific/init_sound.h"
#include "specific/output.h"
#include "specific/texture.h"
#include "specific/winvid.h"
#include "global/vars.h"
#define REQ_SCRIPT_VERSION (3)
#define DESCRIPTION_LENGTH (256)
#define REQ_GAME_STR_COUNT (89)
#define SPECIFIC_STR_COUNT (41)
#define READ_STRINGS(count, lpTable, lpBuffer, lpRead, hFile, failLabel) { \
if( NULL == ((lpTable)=(char **)GlobalAlloc(GMEM_FIXED, sizeof(char*) * (count))) || \
!Read_Strings((count), (lpTable), (lpBuffer), (lpRead), (hFile)) ) goto failLabel; \
}
#ifdef FEATURE_GOLD
extern bool IsGold();
#endif
#ifdef FEATURE_BACKGROUND_IMPROVED
#include "modding/background_new.h"
extern bool LoadingScreensEnabled;
#endif // FEATURE_BACKGROUND_IMPROVED
#if defined(FEATURE_MOD_CONFIG) || defined(FEATURE_VIDEOFX_IMPROVED)
#include "modding/mod_utils.h"
#endif // defined(FEATURE_MOD_CONFIG) || defined(FEATURE_VIDEOFX_IMPROVED)
#ifdef FEATURE_HUD_IMPROVED
#include "modding/texture_utils.h"
#endif // FEATURE_HUD_IMPROVED
#ifdef FEATURE_VIDEOFX_IMPROVED
static bool MarkSemitransPoly(__int16 *ptrObj, int vtxCount, bool colored, LPVOID param) {
UINT16 index = ptrObj[vtxCount];
if( colored ) {
GamePalette16[index >> 8].peFlags = 1; // semitransparent blending mode 1
} else {
PhdTextureInfo[index].drawtype = DRAW_Semitrans;
}
return true;
}
static bool MarkSemitransMesh(int objID, int meshIdx, POLYFILTER *filter) {
if( objID < 0 ) return false;
__int16 *ptrObj = NULL;
// if mesh index is negative, then it's a static mesh
if( meshIdx < 0 ) {
if( (DWORD)objID >= ARRAY_SIZE(StaticObjects) ) return false;
STATIC_INFO *obj = &StaticObjects[objID];
if( !CHK_ANY(obj->flags, 2) ) return false; // no such drawable static for patching
ptrObj = MeshPtr[obj->meshIndex];
} else {
if( (DWORD)objID >= ARRAY_SIZE(Objects) ) return false;
OBJECT_INFO *obj = &Objects[objID];
if( !obj->loaded || meshIdx >= obj->nMeshes ) return false; // no such object/mesh for patching
ptrObj = MeshPtr[obj->meshIndex + meshIdx];
}
return EnumeratePolys(ptrObj, false, MarkSemitransPoly, filter, NULL);
}
static void MarkSemitransObjects() {
#ifdef FEATURE_MOD_CONFIG
// Check if config is presented
if( IsModSemitransConfigLoaded() ) {
POLYFILTER_NODE *node = NULL;
POLYFILTER_NODE **obj = GetModSemitransObjectsFilter();
for( int i=0; inext ) {
MarkSemitransMesh(i, node->id, &node->filter);
}
}
for( node = GetModSemitransStaticsFilter(); node != NULL; node = node->next ) {
MarkSemitransMesh(node->id, -1, &node->filter);
}
for( node = GetModSemitransRoomsFilter(); node != NULL; node = node->next ) {
if( node->id >= 0 && node->id < RoomCount ) {
EnumeratePolys(RoomInfo[node->id].data, true, MarkSemitransPoly, &node->filter, NULL);
}
}
return;
}
#endif // FEATURE_MOD_CONFIG
// If config is absent or disabled, use hardcoded params
static POLYFILTER SkidooFastFilter = {
.n_vtx = 59, .n_gt4 = 14, .n_gt3 = 104, .n_g4 = 0, .n_g3 = 0,
.gt4 = {{~0,~0}}, // no semitrans textured quads
.gt3 = {{48, 4}, {54, 18}, {73, 6}, {0, 0}},
.g4 = {{~0,~0}}, // no semitrans colored quads
.g3 = {{~0,~0}}, // no semitrans colored triangles
};
static POLYFILTER DetailOptionFilter = {
.n_vtx = 80, .n_gt4 = 66, .n_gt3 = 4, .n_g4 = 2, .n_g3 = 0,
.gt4 = {{23, 8}, {44, 8}, {0, 0}},
.gt3 = {{0, 0}}, // all textured triangles are semitrans
.g4 = {{~0,~0}}, // no semitrans colored quads
.g3 = {{~0,~0}}, // no semitrans colored triangles
};
static POLYFILTER GlassOnSinkFilter_Home = {
.n_vtx = 46, .n_gt4 = 41, .n_gt3 = 0, .n_g4 = 0, .n_g3 = 0,
.gt4 = {{17, 9}, {0, 0}},
.gt3 = {{~0,~0}}, // no semitrans textured triangles
.g4 = {{~0,~0}}, // no semitrans colored quads
.g3 = {{~0,~0}}, // no semitrans colored triangles
};
static POLYFILTER GlassOnSinkFilter_Vegas = {
.n_vtx = 46, .n_gt4 = 60, .n_gt3 = 0, .n_g4 = 0, .n_g3 = 0,
.gt4 = {{23, 10}, {0, 0}},
.gt3 = {{~0,~0}}, // no semitrans textured triangles
.g4 = {{~0,~0}}, // no semitrans colored quads
.g3 = {{~0,~0}}, // no semitrans colored triangles
};
MarkSemitransMesh(ID_SKIDOO_FAST, 0, &SkidooFastFilter);
MarkSemitransMesh(ID_DETAIL_OPTION, 0, &DetailOptionFilter);
MarkSemitransMesh(ID_SPHERE_OF_DOOM1, 0, NULL);
MarkSemitransMesh(ID_SPHERE_OF_DOOM2, 0, NULL);
MarkSemitransMesh(ID_FLARE_FIRE, 0, NULL);
MarkSemitransMesh(ID_GUN_FLASH, 0, NULL);
MarkSemitransMesh(ID_M16_FLASH, 0, NULL);
MarkSemitransMesh(21, -1, &GlassOnSinkFilter_Home); // Lara's Home / Home Sweet Home
MarkSemitransMesh(0, -1, &GlassOnSinkFilter_Vegas); // Nightmare in Vegas
}
static void MarkSemitransTextureRanges() {
POLYINDEX *filter = NULL;
__int16 *ptr = AnimatedTextureRanges;
#ifdef FEATURE_MOD_CONFIG
filter = GetModSemitransAnimtexFilter();
// Check if filter is presented
if( filter != NULL && (filter[0].idx || filter[0].num) ) {
int polyIndex = 0;
int polyNumber = *(ptr++);
for( int i=0; i= polyNumber ) {
return;
}
int skip = filter[i].idx - polyIndex;
polyIndex += skip;
if( polyIndex >= polyNumber ) {
return;
}
while( skip-- > 0 ) {
int len = 1 + *(ptr++);
ptr += len;
}
int number = MIN(filter[i].num, polyNumber - polyIndex);
polyIndex += number;
while( number-- > 0 ) {
for( int j = *(ptr++); j>=0; --j, ++ptr ) {
PhdTextureInfo[*ptr].drawtype = DRAW_Semitrans;
}
}
}
return;
}
#endif // FEATURE_MOD_CONFIG
// If filter is absent or disabled, do it in automatic mode
for( int i = *(ptr++); i>0; --i ) {
for( int j = *(ptr++); j>=0; --j, ++ptr ) {
if( filter != NULL || PhdTextureInfo[*ptr].drawtype == DRAW_ColorKey ) {
// all animated room textures with colorkey are supposed to be semitransparent
PhdTextureInfo[*ptr].drawtype = DRAW_Semitrans;
}
}
}
}
void UpdateDepthQ(bool isReset) {
static DEPTHQ_ENTRY depthQBackup[15];
if( isReset ) {
memcpy(depthQBackup, DepthQTable, sizeof(DEPTHQ_ENTRY) * 15);
return;
}
switch( SavedAppSettings.LightingMode ) {
case 0:
for( DWORD i = 0; i < 15; ++i ) {
DepthQTable[i] = DepthQTable[15];
}
break;
case 1:
memcpy(&DepthQTable[7], &depthQBackup[7], sizeof(DEPTHQ_ENTRY) * 8);
for( DWORD i = 0; i < 7; ++i ) {
DepthQTable[i] = DepthQTable[7];
}
break;
case 2:
memcpy(DepthQTable, depthQBackup, sizeof(DEPTHQ_ENTRY) * 15);
break;
}
}
#endif // FEATURE_VIDEOFX_IMPROVED
#if defined(FEATURE_MOD_CONFIG)
bool BarefootSfxEnabled = true;
static void LoadBareFootSFX(int *sampleIndexes, int sampleCount) {
if( !BarefootSfxEnabled || !IsModBarefoot() || !sampleIndexes || sampleCount < 1 ) return;
LPCTSTR sfxFileName = GetFullPath("data\\barefoot.sfx");
if( !PathFileExists(sfxFileName) ) return;
HANDLE hSfxFile = CreateFile(sfxFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hSfxFile == INVALID_HANDLE_VALUE ) return;
int i, j;
for( i=0, j=0; i < sampleCount; ++j ) {
DWORD bytesRead;
WAVEPCM_HEADER waveHeader;
ReadFileSync(hSfxFile, &waveHeader, sizeof(WAVEPCM_HEADER), &bytesRead, NULL);
if( waveHeader.dwRiffChunkID != 0x46464952 || // "RIFF"
waveHeader.dwFormat != 0x45564157 || // "WAVE"
waveHeader.dwDataSubchunkID != 0x61746164 ) // "data"
{
CloseHandle(hSfxFile);
return;
}
DWORD dataSize = (waveHeader.dwDataSubchunkSize + 1) & ~1; // aligned data size
LPWAVEFORMATEX waveFormat = (LPWAVEFORMATEX)&waveHeader.wFormatTag;
waveFormat->cbSize = 0;
if( sampleIndexes[i] == j ) {
LPVOID waveData = game_malloc(dataSize, GBUF_Samples);
ReadFileSync(hSfxFile, waveData, dataSize, &bytesRead, NULL);
WinSndMakeSample(i, waveFormat, waveData, dataSize);
game_free(dataSize);
++i;
} else {
SetFilePointer(hSfxFile, dataSize, NULL, FILE_CURRENT);
}
}
for( i=0; i<4; ++i ) { // there are no more than 4 barefoot step samples
if( SampleInfos[i].sfxID >= 4 ) break;
// SFX parameters are taken from the PlayStation version
SampleInfos[i].volume = 0x3332;
SampleInfos[i].randomness = 0;
SampleInfos[i].flags = 0x6010;
}
CloseHandle(hSfxFile);
}
#endif // FEATURE_MOD_CONFIG
#ifdef FEATURE_BACKGROUND_IMPROVED
int PatternTexPage = -1;
static struct {
int x, y, side, page;
} BgndPattern = {0, 0, 0, -1};
static bool GetBgndPatternInfo() {
memset(&BgndPattern, 0, sizeof(BgndPattern));
if( !Objects[ID_INV_BACKGROUND].loaded ) {
return false;
}
__int16 *meshPtr = MeshPtr[Objects[ID_INV_BACKGROUND].meshIndex];
meshPtr += 3+2; // skip mesh coords (3*INT16) and radius (1*INT32)
int num = *(meshPtr++);
meshPtr += num*3; // skip vertices (each one is 3xINT16)
num = *(meshPtr++);
if( num >= 0 ) // negative num means lights instead of normals
meshPtr += num*3; // skip normals (each is 3xINT16)
else
meshPtr -= num; // skip lights (each one is INT16)
num = *(meshPtr++); // get quads number (we need at least one)
if( num < 1 ) return false;
meshPtr += 4; // skip 4 vertex indices of 1st textured quad (each one is INT16)
DWORD textureIndex = *(meshPtr++); // get texture index of 1st textured quad.
PHD_TEXTURE *texture = &PhdTextureInfo[textureIndex];
PHD_UV *uv = texture->uv;
if( uv[0].u != uv[3].u && uv[1].u != uv[2].u && uv[0].u >= uv[2].u &&
uv[0].v != uv[1].v && uv[2].v != uv[3].v && uv[0].v >= uv[2].v )
{
return false;
}
int x = (uv[0].u % 0x100 + uv[0].u) / 0x100;
int y = (uv[0].v % 0x100 + uv[0].v) / 0x100;
int w = (uv[2].u % 0x100 + uv[2].u) / 0x100 - x;
int h = (uv[2].v % 0x100 + uv[2].v) / 0x100 - y;
if( w != h ) return false;
while( h%2 == 0 ) h /= 2;
if( h != 1 ) return false;
BgndPattern.x = x;
BgndPattern.y = y;
BgndPattern.side = w;
BgndPattern.page = texture->tpage;
return true;
}
static int CreateBgndPatternTexture(HANDLE hFile) {
if( hFile == INVALID_HANDLE_VALUE || SavedAppSettings.RenderMode != RM_Hardware || BgndPattern.side <= 0 ) {
return -1;
}
int pageIndex = -1;
#if (DIRECT3D_VERSION >= 0x900)
if( PathFileExists("textures/background.png") ) {
pageIndex = AddExternalTexture("textures/background.png", true);
if( pageIndex >= 0 ) {
HWR_TexturePageIndexes[HwrTexturePagesCount] = pageIndex;
HWR_PageHandles[HwrTexturePagesCount] = GetTexturePageHandle(pageIndex);
pageIndex = HwrTexturePagesCount++;
return pageIndex;
}
}
#endif // (DIRECT3D_VERSION >= 0x900)
DWORD bytesRead;
int pageCount = 0;
SetFilePointer(hFile, LevelFileTexPagesOffset, NULL, FILE_BEGIN);
ReadFileSync(hFile, &pageCount, sizeof(pageCount), &bytesRead, NULL);
if( BgndPattern.page >= pageCount ) {
return -1;
}
#if (DIRECT3D_VERSION >= 0x900)
if( IsExternalTexture(BgndPattern.page) ) {
return -1;
}
#endif // (DIRECT3D_VERSION >= 0x900)
DWORD pageSize = ( TextureFormat.bpp < 16 ) ? 256*256*1 : 256*256*2;
BYTE *bitmap = (BYTE *)GlobalAlloc(GMEM_FIXED, pageSize);
if( TextureFormat.bpp < 16 ) {
SetFilePointer(hFile, BgndPattern.page*(256*256*1), NULL, FILE_CURRENT);
ReadFileSync(hFile, bitmap, pageSize, &bytesRead, NULL);
pageIndex = MakeCustomTexture(BgndPattern.x, BgndPattern.y, BgndPattern.side, BgndPattern.side,
256, BgndPattern.side, 8, bitmap, GamePalette8, PaletteIndex, NULL, false);
} else {
SetFilePointer(hFile, pageCount*(256*256*1) + BgndPattern.page*(256*256*2), NULL, FILE_CURRENT);
ReadFileSync(hFile, bitmap, pageSize, &bytesRead, NULL);
pageIndex = MakeCustomTexture(BgndPattern.x, BgndPattern.y, BgndPattern.side, BgndPattern.side,
256, BgndPattern.side, 16, bitmap, NULL, -1, NULL, false);
}
if( pageIndex >= 0 ) {
HWR_TexturePageIndexes[HwrTexturePagesCount] = pageIndex;
HWR_PageHandles[HwrTexturePagesCount] = GetTexturePageHandle(pageIndex);
pageIndex = HwrTexturePagesCount++;
}
GlobalFree(bitmap);
return pageIndex;
}
#endif // FEATURE_BACKGROUND_IMPROVED
static GF_LEVEL_TYPE LoadLevelType = GFL_NOLEVEL;
BOOL __cdecl ReadFileSync(HANDLE hFile, LPVOID lpBuffer, DWORD nBytesToRead, LPDWORD lpnBytesRead, LPOVERLAPPED lpOverlapped) {
ReadFileBytesCounter += nBytesToRead;
if( ReadFileBytesCounter > 0x4000 ) {
ReadFileBytesCounter = 0;
WinVidSpinMessageLoop(false);
}
return ReadFile(hFile, lpBuffer, nBytesToRead, lpnBytesRead, lpOverlapped);
}
BOOL __cdecl LoadTexturePages(HANDLE hFile) {
int i, pageCount;
DWORD bytesRead;
DWORD pageSize;
LPVOID texPageBuffer;
BYTE *texPagePtr;
ReadFileSync(hFile, &pageCount, sizeof(pageCount), &bytesRead, NULL);
// for software renderer read 8bit texture pages to GAME allocated buffer and skip 16bit pages
if( SavedAppSettings.RenderMode == RM_Software ) {
for( i=0; i 0x400 ) {
lstrcpy(StringToShow, "LoadRoom(): Too many rooms");
return FALSE;
}
// Allocate memory for room info
RoomInfo = (ROOM_INFO *)game_malloc(sizeof(ROOM_INFO)*RoomCount, GBUF_RoomInfos);
if( RoomInfo == NULL ) {
lstrcpy(StringToShow, "LoadRoom(): Could not allocate memory for rooms");
return FALSE;
}
// For every room read info
for( int i = 0; i < RoomCount; ++i ) {
// Room position
ReadFileSync(hFile, &RoomInfo[i].x, sizeof(int), &bytesRead, NULL);
ReadFileSync(hFile, &RoomInfo[i].z, sizeof(int), &bytesRead, NULL);
RoomInfo[i].y = 0;
// Room floor/ceiling
ReadFileSync(hFile, &RoomInfo[i].minFloor, sizeof(int), &bytesRead, NULL);
ReadFileSync(hFile, &RoomInfo[i].maxCeiling, sizeof(int), &bytesRead, NULL);
// Room mesh
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
RoomInfo[i].data = (__int16 *)game_malloc(sizeof(__int16)*dwCount, GBUF_RoomMesh);
ReadFileSync(hFile, RoomInfo[i].data, sizeof(__int16)*dwCount, &bytesRead, NULL);
// Doors
ReadFileSync(hFile, &wCount, sizeof(__int16), &bytesRead, NULL);
if( wCount == 0 ) {
RoomInfo[i].doors = NULL;
} else {
RoomInfo[i].doors = (DOOR_INFOS *)game_malloc(sizeof(__int16) + sizeof(DOOR_INFO)*wCount, GBUF_RoomDoor);
RoomInfo[i].doors->wCount = wCount;
ReadFileSync(hFile, &RoomInfo[i].doors->door, sizeof(DOOR_INFO)*wCount, &bytesRead, NULL);
}
// Room floor
ReadFileSync(hFile, &RoomInfo[i].xSize, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &RoomInfo[i].ySize, sizeof(__int16), &bytesRead, NULL);
dwCount = RoomInfo[i].xSize * RoomInfo[i].ySize;
RoomInfo[i].floor = (FLOOR_INFO *)game_malloc(sizeof(FLOOR_INFO)*dwCount, GBUF_RoomFloor);
ReadFileSync(hFile, RoomInfo[i].floor, sizeof(FLOOR_INFO)*dwCount, &bytesRead, NULL);
// Room lights
ReadFileSync(hFile, &RoomInfo[i].ambient1, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &RoomInfo[i].ambient2, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &RoomInfo[i].lightMode, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &RoomInfo[i].numLights, sizeof(__int16), &bytesRead, NULL);
if( RoomInfo[i].numLights == 0 ) {
RoomInfo[i].light = NULL;
} else {
RoomInfo[i].light = (LIGHT_INFO *)game_malloc(sizeof(LIGHT_INFO)*RoomInfo[i].numLights, GBUF_RoomLights);
ReadFileSync(hFile, RoomInfo[i].light, sizeof(LIGHT_INFO)*RoomInfo[i].numLights, &bytesRead, NULL);
}
// Static mesh infos
ReadFileSync(hFile, &RoomInfo[i].numMeshes, sizeof(__int16), &bytesRead, NULL);
if( RoomInfo[i].numMeshes == 0 ) {
RoomInfo[i].mesh = NULL;
} else {
RoomInfo[i].mesh = (MESH_INFO *)game_malloc(sizeof(MESH_INFO)*RoomInfo[i].numMeshes, GBUF_RoomStaticMeshInfos);
ReadFileSync(hFile, RoomInfo[i].mesh, sizeof(MESH_INFO)*RoomInfo[i].numMeshes, &bytesRead, NULL);
}
// Flipped (alternative) room
ReadFileSync(hFile, &RoomInfo[i].flippedRoom, sizeof(__int16), &bytesRead, NULL);
// Room flags
ReadFileSync(hFile, &RoomInfo[i].flags, sizeof(__int16), &bytesRead, NULL);
// Initialise some variables
RoomInfo[i].boundActive = 0;
RoomInfo[i].boundLeft = PhdWinMaxX;
RoomInfo[i].boundTop = PhdWinMaxY;
RoomInfo[i].boundRight = 0;
RoomInfo[i].boundBottom = 0;
RoomInfo[i].itemNumber = -1;
RoomInfo[i].fxNumber = -1;
}
// Read floor data
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
FloorData = (__int16 *)game_malloc(sizeof(__int16)*dwCount, GBUF_FloorData);
ReadFileSync(hFile, FloorData, sizeof(__int16)*dwCount, &bytesRead, NULL);
return TRUE;
}
void __cdecl AdjustTextureUVs(bool resetUvAdd) {
DWORD i, j;
int offset;
BYTE uvFlags;
PHD_UV *pUV, *pBackup;
// NOTE: there was no such backup in the original game
extern PHD_TEXTURE TextureBackupUV[ARRAY_SIZE(PhdTextureInfo)];
if( resetUvAdd ) {
memcpy(TextureBackupUV, PhdTextureInfo, TextureInfoCount * sizeof(PHD_TEXTURE));
}
#if (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.RenderMode == RM_Hardware ) {
double forcedAdjust = GetTexPagesAdjustment();
if( forcedAdjust > 0.0) {
UvAdd = (int)(forcedAdjust * 256.0);
for( i=0; i>= 2;
}
}
return;
}
}
#endif // (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.RenderMode == RM_Hardware && (SavedAppSettings.TexelAdjustMode == TAM_Always ||
(SavedAppSettings.TexelAdjustMode == TAM_BilinearOnly && SavedAppSettings.BilinearFiltering)) )
{
UvAdd = SavedAppSettings.LinearAdjustment;
} else {
UvAdd = SavedAppSettings.NearestAdjustment;
}
for( i=0; i>= 2;
}
}
}
BOOL __cdecl LoadObjects(HANDLE hFile) {
DWORD i, j;
DWORD bytesRead;
DWORD dwCount;
DWORD animCount;
DWORD animOffset;
DWORD objNumber;
UINT16 *uv;
// Load mesh base data
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
Meshes = (__int16 *)game_malloc(sizeof(__int16)*dwCount, GBUF_Meshes);
ReadFileSync(hFile, Meshes, sizeof(__int16)*dwCount, &bytesRead, NULL);
// Load mesh pointers
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
MeshPtr = (__int16 **)game_malloc(sizeof(__int16 *)*dwCount, GBUF_MeshPointers);
ReadFileSync(hFile, MeshPtr, sizeof(__int16 *)*dwCount, &bytesRead, NULL);
// Remap mesh pointers
for( i = 0; i < dwCount; ++i )
MeshPtr[i] = (__int16 *)((DWORD)Meshes + (DWORD)MeshPtr[i]);
// Load anims
ReadFileSync(hFile, &animCount, sizeof(DWORD), &bytesRead, NULL);
Anims = (ANIM_STRUCT *)game_malloc(sizeof(ANIM_STRUCT)*animCount, GBUF_Anims);
ReadFileSync(hFile, Anims, sizeof(ANIM_STRUCT)*animCount, &bytesRead, NULL);
// Load changes
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
AnimChanges = (CHANGE_STRUCT *)game_malloc(sizeof(CHANGE_STRUCT)*dwCount, GBUF_Structs);
ReadFileSync(hFile, AnimChanges, sizeof(CHANGE_STRUCT)*dwCount, &bytesRead, NULL);
// Load ranges
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
AnimRanges = (RANGE_STRUCT *)game_malloc(sizeof(RANGE_STRUCT)*dwCount, GBUF_Ranges);
ReadFileSync(hFile, AnimRanges, sizeof(RANGE_STRUCT)*dwCount, &bytesRead, NULL);
// Load commands
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
AnimCommands = (__int16 *)game_malloc(sizeof(__int16)*dwCount, GBUF_Commands);
ReadFileSync(hFile, AnimCommands, sizeof(__int16)*dwCount, &bytesRead, NULL);
// Load bones
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
AnimBones = (int *)game_malloc(sizeof(int)*dwCount, GBUF_Bones);
ReadFileSync(hFile, AnimBones, sizeof(int)*dwCount, &bytesRead, NULL);
// Load frames
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
AnimFrames = (__int16 *)game_malloc(sizeof(__int16)*dwCount, GBUF_Frames);
ReadFileSync(hFile, AnimFrames, sizeof(__int16)*dwCount, &bytesRead, NULL);
// Remap anim pointers
for( i = 0; i < animCount; ++i )
Anims[i].framePtr = (__int16 *)((DWORD)AnimFrames + (DWORD)Anims[i].framePtr);
// Load animated objects
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
for( i = 0; i < dwCount; ++i ) {
ReadFileSync(hFile, &objNumber, sizeof(DWORD), &bytesRead, NULL);
ReadFileSync(hFile, &Objects[objNumber].nMeshes, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &Objects[objNumber].meshIndex, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &Objects[objNumber].boneIndex, sizeof(int), &bytesRead, NULL);
ReadFileSync(hFile, &animOffset, sizeof(DWORD), &bytesRead, NULL);
ReadFileSync(hFile, &Objects[objNumber].animIndex, sizeof(__int16), &bytesRead, NULL);
Objects[objNumber].frameBase = (__int16 *)((DWORD)AnimFrames + animOffset);
Objects[objNumber].loaded = 1;
}
// Initialise animated objects
InitialiseObjects();
// Load static objects
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
#ifdef FEATURE_VIDEOFX_IMPROVED
memset(&StaticObjects, 0, sizeof(StaticObjects)); // NOTE: we need to be sure that a static object is really loaded
#endif // FEATURE_VIDEOFX_IMPROVED
for( i = 0; i < dwCount; ++i ) {
ReadFileSync(hFile, &objNumber, sizeof(DWORD), &bytesRead, NULL);
ReadFileSync(hFile, &StaticObjects[objNumber].meshIndex, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &StaticObjects[objNumber].drawBounds, sizeof(STATIC_BOUNDS), &bytesRead, NULL);
ReadFileSync(hFile, &StaticObjects[objNumber].collisionBounds, sizeof(STATIC_BOUNDS), &bytesRead, NULL);
ReadFileSync(hFile, &StaticObjects[objNumber].flags, sizeof(UINT16), &bytesRead, NULL);
}
// Load textures info
ReadFileSync(hFile, &TextureInfoCount, sizeof(DWORD), &bytesRead, NULL);
if( TextureInfoCount > ARRAY_SIZE(PhdTextureInfo) ) {
lstrcpy(StringToShow, "Too many Textures in level");
return FALSE;
}
ReadFileSync(hFile, PhdTextureInfo, sizeof(PHD_TEXTURE)*TextureInfoCount, &bytesRead, NULL);
for( i = 0; i < TextureInfoCount; ++i ) {
LabTextureUVFlags[i] = 0;
uv = &PhdTextureInfo[i].uv[0].u;
for( j = 0; j < 8; ++j ) {
if( (uv[j] & 0x0080) != 0 ) {
uv[j] |= 0x00FF;
LabTextureUVFlags[i] |= (1 << j);
} else {
uv[j] &= 0xFF00;
}
}
}
#ifdef FEATURE_BACKGROUND_IMPROVED
// get background pattern info before UV adjustment
GetBgndPatternInfo();
#endif // FEATURE_BACKGROUND_IMPROVED
AdjustTextureUVs(true);
return TRUE;
}
BOOL __cdecl LoadSprites(HANDLE hFile) {
DWORD bytesRead;
DWORD objNumber;
DWORD dwCount;
// Load sprite infos
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
ReadFileSync(hFile, PhdSpriteInfo, sizeof(PHD_SPRITE)*dwCount, &bytesRead, NULL);
// Assign sprites to objects
ReadFileSync(hFile, &dwCount, sizeof(DWORD), &bytesRead, NULL);
for( DWORD i = 0; i < dwCount; ++i ) {
ReadFileSync(hFile, &objNumber, sizeof(DWORD), &bytesRead, NULL);
if ( objNumber < ID_NUMBER_OBJECTS ) {
ReadFileSync(hFile, &Objects[objNumber].nMeshes, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &Objects[objNumber].meshIndex, sizeof(__int16), &bytesRead, NULL);
Objects[objNumber].loaded = 1;
} else {
objNumber -= ID_NUMBER_OBJECTS;
SetFilePointer(hFile, sizeof(__int16), NULL, FILE_CURRENT); // StaticObjects don't have nMeshes (just one mesh)
ReadFileSync(hFile, &StaticObjects[objNumber].meshIndex, sizeof(__int16), &bytesRead, NULL);
}
}
return TRUE;
}
BOOL __cdecl LoadItems(HANDLE hFile) {
DWORD itemsCount, bytesRead;
ReadFileSync(hFile, &itemsCount, sizeof(DWORD), &bytesRead, NULL);
if( itemsCount == 0 )
return TRUE;
if( itemsCount > 256 ) {
lstrcpy(StringToShow, "LoadItems(): Too Many Items being Loaded!!");
return FALSE;
}
Items = (ITEM_INFO *)game_malloc(sizeof(ITEM_INFO)*256, GBUF_Items);
if( Items == NULL ) {
lstrcpy(StringToShow, "LoadItems(): Unable to allocate memory for 'items'");
return FALSE;
}
LevelItemCount = itemsCount;
InitialiseItemArray(256);
for( DWORD i = 0; i < itemsCount; ++i ) {
ReadFileSync(hFile, &Items[i].objectID, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &Items[i].roomNumber, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &Items[i].pos.x, sizeof(int), &bytesRead, NULL);
ReadFileSync(hFile, &Items[i].pos.y, sizeof(int), &bytesRead, NULL);
ReadFileSync(hFile, &Items[i].pos.z, sizeof(int), &bytesRead, NULL);
ReadFileSync(hFile, &Items[i].pos.rotY, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &Items[i].shade1, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &Items[i].shade2, sizeof(__int16), &bytesRead, NULL);
ReadFileSync(hFile, &Items[i].flags, sizeof(UINT16), &bytesRead, NULL);
if( Items[i].objectID < 0 || Items[i].objectID >= ID_NUMBER_OBJECTS ) {
wsprintf(StringToShow, "LoadItems(): Bad Object number (%d) on Item %d", Items[i].objectID, i);
return FALSE;
}
InitialiseItem(i);
}
return TRUE;
}
BOOL __cdecl LoadDepthQ(HANDLE hFile) {
int i, j;
DWORD bytesRead;
#if (DIRECT3D_VERSION < 0x900)
RGB888 paletteBuffer[256];
#endif // (DIRECT3D_VERSION < 0x900)
ReadFileSync(hFile, DepthQTable, 32*sizeof(DEPTHQ_ENTRY), &bytesRead, NULL);
for( i=0; i<32; ++i )
DepthQTable[i].index[0] = 0;
#if (DIRECT3D_VERSION >= 0x900)
memcpy(DepthQIndex, &DepthQTable[24], sizeof(DEPTHQ_ENTRY));
#else // (DIRECT3D_VERSION >= 0x900)
if( GameVid_IsWindowedVga ) {
CopyBitmapPalette(GamePalette8, DepthQTable[0].index, 32*sizeof(DEPTHQ_ENTRY), paletteBuffer);
SyncSurfacePalettes(DepthQTable, 256, 32, 256, GamePalette8, DepthQTable, 256, paletteBuffer, true);
memcpy(GamePalette8, paletteBuffer, sizeof(GamePalette8));
for( i=0; i<256; ++i ) {
DepthQIndex[i] = S_COLOUR(GamePalette8[i].red, GamePalette8[i].green, GamePalette8[i].blue);
}
} else {
memcpy(DepthQIndex, &DepthQTable[24], sizeof(DEPTHQ_ENTRY));
}
#endif // (DIRECT3D_VERSION >= 0x900)
#ifdef FEATURE_VIDEOFX_IMPROVED
UpdateDepthQ(true);
#endif // FEATURE_VIDEOFX_IMPROVED
for( i=0; i<32; ++i ) {
for( j=0; j<256; ++j ) {
GouraudTable[j].index[i] = DepthQTable[i].index[j];
}
}
IsWet = 0;
for( i=0; i<256; ++i ) {
WaterPalette[i].red = GamePalette8[i].red * 2 / 3;
WaterPalette[i].green = GamePalette8[i].green * 2 / 3;
WaterPalette[i].blue = GamePalette8[i].blue;
}
return TRUE;
}
BOOL __cdecl LoadPalettes(HANDLE hFile) {
DWORD bytesRead;
ReadFileSync(hFile, GamePalette8, 256*sizeof(RGB888), &bytesRead, NULL);
GamePalette8[0].red = 0;
GamePalette8[0].green = 0;
GamePalette8[0].blue = 0;
for( int i=1; i<256; ++i ) {
// NOTE: the original code just shifts left 2 bits. But this way is slightly better
GamePalette8[i].red = (GamePalette8[i].red << 2) | (GamePalette8[i].red >> 4);
GamePalette8[i].green = (GamePalette8[i].green << 2) | (GamePalette8[i].green >> 4);
GamePalette8[i].blue = (GamePalette8[i].blue << 2) | (GamePalette8[i].blue >> 4);
}
ReadFileSync(hFile, GamePalette16, 256*sizeof(PALETTEENTRY), &bytesRead, NULL);
#if (DIRECT3D_VERSION >= 0x900)
if( !IsTexPagesLegacyColors() ) {
for( int i=0; i<256; ++i ) {
PALETTEENTRY *pal = &GamePalette16[i];
pal->peRed = (pal->peRed & 0xF8) | (pal->peRed >> 5);
pal->peGreen = (pal->peGreen & 0xF8) | (pal->peGreen >> 5);
pal->peBlue = (pal->peBlue & 0xF8) | (pal->peBlue >> 5);
}
}
#endif // (DIRECT3D_VERSION >= 0x900)
return TRUE;
}
BOOL __cdecl LoadCameras(HANDLE hFile) {
DWORD bytesRead;
ReadFileSync(hFile, &CameraCount, sizeof(DWORD), &bytesRead, NULL);
if( CameraCount != 0 ) {
Camera.fixed = (OBJECT_VECTOR *)game_malloc(sizeof(OBJECT_VECTOR)*CameraCount, GBUF_Cameras);
if ( Camera.fixed == NULL ) {
return FALSE;
}
ReadFileSync(hFile, Camera.fixed, sizeof(OBJECT_VECTOR)*CameraCount, &bytesRead, NULL);
}
return TRUE;
}
BOOL __cdecl LoadSoundEffects(HANDLE hFile) {
DWORD bytesRead;
ReadFileSync(hFile, &SoundFxCount, sizeof(DWORD), &bytesRead, NULL);
if( SoundFxCount != 0 ) {
SoundFx = (OBJECT_VECTOR *)game_malloc(sizeof(OBJECT_VECTOR)*SoundFxCount, GBUF_SoundFX);
if( SoundFx == NULL ) {
return FALSE;
}
ReadFileSync(hFile, SoundFx, sizeof(OBJECT_VECTOR)*SoundFxCount, &bytesRead, NULL);
}
return TRUE;
}
BOOL __cdecl LoadBoxes(HANDLE hFile) {
DWORD overlapsCount, bytesRead;
// Load Boxes
ReadFileSync(hFile, &BoxesCount, sizeof(DWORD), &bytesRead, NULL);
Boxes = (BOX_INFO *)game_malloc(sizeof(BOX_INFO)*BoxesCount, GBUF_Boxes);
ReadFileSync(hFile, Boxes, sizeof(BOX_INFO)*BoxesCount, &bytesRead, NULL);
if( bytesRead != sizeof(BOX_INFO)*BoxesCount ) {
lstrcpy(StringToShow, "LoadBoxes(): Unable to load boxes");
return FALSE;
}
// Load Overlaps
ReadFileSync(hFile, &overlapsCount, sizeof(DWORD), &bytesRead, NULL);
Overlaps = (UINT16 *)game_malloc(sizeof(UINT16)*overlapsCount, GBUF_Overlaps);
ReadFileSync(hFile, Overlaps, sizeof(UINT16)*overlapsCount, &bytesRead, NULL);
if( bytesRead != sizeof(UINT16)*overlapsCount ) {
lstrcpy(StringToShow, "LoadBoxes(): Unable to load box overlaps");
return FALSE;
}
// Load GroundZones and FlyZones
for( int i=0; i<2; ++i ) {
for( int j=0; j<4; ++j ) {
if( (j == 2) ||
(j == 1 && !Objects[ID_SPIDER_or_WOLF].loaded && !Objects[ID_SKIDOO_ARMED].loaded) ||
(j == 3 && !Objects[ID_YETI].loaded && !Objects[ID_WORKER3].loaded) )
{
SetFilePointer(hFile, sizeof(__int16)*BoxesCount, NULL, FILE_CURRENT); // skip some GroundZones
continue;
}
GroundZones[j*2+i] = (__int16 *)game_malloc(sizeof(__int16)*BoxesCount, GBUF_GroundZone);
ReadFileSync(hFile, GroundZones[j*2+i], sizeof(__int16)*BoxesCount, &bytesRead, NULL);
if( bytesRead != sizeof(__int16)*BoxesCount ) {
lstrcpy(StringToShow, "LoadBoxes(): Unable to load 'ground_zone'");
return FALSE;
}
}
FlyZones[i] = (__int16 *)game_malloc(sizeof(__int16)*BoxesCount, GBUF_FlyZone);
ReadFileSync(hFile, FlyZones[i], sizeof(__int16)*BoxesCount, &bytesRead, NULL);
if( bytesRead != sizeof(__int16)*BoxesCount ) {
lstrcpy(StringToShow, "LoadBoxes(): Unable to load 'fly_zone'");
return FALSE;
}
}
return TRUE;
}
BOOL __cdecl LoadAnimatedTextures(HANDLE hFile) {
DWORD animTexCount, bytesRead;
ReadFileSync(hFile, &animTexCount, sizeof(DWORD), &bytesRead, NULL);
AnimatedTextureRanges = (__int16 *)game_malloc(sizeof(__int16)*animTexCount, GBUF_AnimatingTextureRanges);
ReadFileSync(hFile, AnimatedTextureRanges, sizeof(__int16)*animTexCount, &bytesRead, NULL);
return TRUE;
}
BOOL __cdecl LoadCinematic(HANDLE hFile) {
DWORD bytesRead;
ReadFileSync(hFile, &CineFramesCount, sizeof(__int16), &bytesRead, NULL);
if( CineFramesCount != 0 ) {
CineFrames = (CINE_FRAME_INFO *)game_malloc(sizeof(CINE_FRAME_INFO)*CineFramesCount, GBUF_CinematicFrames);
ReadFileSync(hFile, CineFrames, sizeof(CINE_FRAME_INFO)*CineFramesCount, &bytesRead, NULL);
IsCinematicLoaded = TRUE;
} else {
IsCinematicLoaded = FALSE;
}
return TRUE;
}
BOOL __cdecl LoadDemo(HANDLE hFile) {
DWORD bytesRead;
__int16 demoSize;
DemoCount = 0;
DemoPtr = game_malloc(36000, GBUF_LoadDemoBuffer);
ReadFileSync(hFile, &demoSize, sizeof(__int16), &bytesRead, NULL);
if( demoSize != 0 ) {
ReadFileSync(hFile, DemoPtr, demoSize, &bytesRead, NULL);
IsDemoLoaded = TRUE;
} else {
IsDemoLoaded = FALSE;
}
return TRUE;
}
void __cdecl LoadDemoExternal(LPCTSTR levelName) {
HANDLE hFile;
DWORD bytesRead = 0;
char fileName[80] = {0};
strcpy(fileName, levelName);
ChangeFileNameExtension(fileName, "DEM");
hFile = CreateFile(fileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile != INVALID_HANDLE_VALUE ) {
ReadFileSync(hFile, DemoPtr, 36000, &bytesRead, NULL);
IsDemoLoaded = ( bytesRead > 0 );
CloseHandle(hFile);
}
}
BOOL __cdecl LoadSamples(HANDLE hFile) {
int i, j;
DWORD bytesRead;
HANDLE hSfxFile;
LPCTSTR sfxFileName;
DWORD dataSize;
LPVOID waveData;
int sampleCount;
WAVEPCM_HEADER waveHeader;
LPWAVEFORMATEX waveFormat;
int sampleIndexes[500];
SoundIsActive = FALSE;
if( !WinSndIsSoundEnabled() ) {
return TRUE;
}
WinSndFreeAllSamples();
// Load Sample Lut
ReadFileSync(hFile, SampleLut, sizeof(SampleLut), &bytesRead, NULL);
// Load Sample Infos
ReadFileSync(hFile, &SampleInfoCount, sizeof(DWORD), &bytesRead, NULL);
if( SampleInfoCount == 0 ) {
return FALSE;
}
SampleInfos = (SAMPLE_INFO *)game_malloc(sizeof(SAMPLE_INFO)*SampleInfoCount, GBUF_SampleInfos);
ReadFileSync(hFile, SampleInfos, sizeof(SAMPLE_INFO)*SampleInfoCount, &bytesRead, NULL);
// Load Samples Count
ReadFileSync(hFile, &sampleCount, sizeof(int), &bytesRead, NULL);
if( sampleCount == 0 ) {
return FALSE;
}
// Load Samples Indexes
ReadFileSync(hFile, sampleIndexes, sizeof(DWORD)*sampleCount, &bytesRead, NULL);
// Open SFX file
sfxFileName = "data\\main.sfx";
#ifdef FEATURE_GOLD
// For the Gold mode use the Gold SFX, if the level is not Title and not Lara's Home
if( IsGold() && LoadLevelType != GFL_TITLE && CurrentLevel != 0 ) {
sfxFileName = "data\\maing.sfx";
}
#endif // FEATURE_GOLD
sfxFileName = GetFullPath(sfxFileName);
hSfxFile = CreateFile(sfxFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hSfxFile == INVALID_HANDLE_VALUE ) {
wsprintf(StringToShow, "Could not open MAIN.SFX file");
return FALSE;
}
for( i=0, j=0; i < sampleCount; ++j ) {
ReadFileSync(hSfxFile, &waveHeader, sizeof(WAVEPCM_HEADER), &bytesRead, NULL);
if( waveHeader.dwRiffChunkID != 0x46464952 || // "RIFF"
waveHeader.dwFormat != 0x45564157 || // "WAVE"
waveHeader.dwDataSubchunkID != 0x61746164 ) // "data"
{
CloseHandle(hSfxFile);
return FALSE;
}
dataSize = (waveHeader.dwDataSubchunkSize + 1) & ~1; // aligned data size
waveFormat = (LPWAVEFORMATEX)&waveHeader.wFormatTag;
waveFormat->cbSize = 0;
if( sampleIndexes[i] == j ) {
waveData = game_malloc(dataSize, GBUF_Samples);
ReadFileSync(hSfxFile, waveData, dataSize, &bytesRead, NULL);
if( !WinSndMakeSample(i, waveFormat, waveData, dataSize) ) {
CloseHandle(hSfxFile);
return FALSE;
}
game_free(dataSize);
++i;
} else {
SetFilePointer(hSfxFile, dataSize, NULL, FILE_CURRENT);
}
}
CloseHandle(hSfxFile);
SoundIsActive = TRUE;
#if defined(FEATURE_MOD_CONFIG)
LoadBareFootSFX(sampleIndexes, sampleCount);
#endif // FEATURE_MOD_CONFIG
return TRUE;
}
void __cdecl ChangeFileNameExtension(char *fileName, const char *fileExt) {
char *fileNamePtr = fileName;
for( ; *fileNamePtr; ++fileNamePtr ) {
if( *fileNamePtr == '.' )
break;
}
fileNamePtr[0] = '.';
fileNamePtr[1] = fileExt[0];
fileNamePtr[2] = fileExt[1];
fileNamePtr[3] = fileExt[2];
fileNamePtr[4] = 0;
}
LPCTSTR __cdecl GetFullPath(LPCTSTR fileName) {
static char fullPathBuffer[136];
#if defined FEATURE_NOCD_DATA
wsprintf(fullPathBuffer, ".\\%s", fileName);
#else // !FEATURE_NOCD_DATA
wsprintf(fullPathBuffer, "%c:\\%s", DriveLetter, fileName);
#endif // FEATURE_NOCD_DATA
return fullPathBuffer;
}
BOOL __cdecl SelectDrive() {
HANDLE hFile;
DWORD driveBitMask;
char fileName[] = "D:\\data\\legal.pcx";
char driveName[] = "A:\\";
DriveLetter = 'A';
for( driveBitMask = GetLogicalDrives(); driveBitMask; driveBitMask >>= 1 ) {
if( (driveBitMask & 1) != 0 ) {
driveName[0] = DriveLetter;
if( GetDriveType(driveName) == DRIVE_CDROM ) {
fileName[0] = DriveLetter;
hFile = CreateFile(fileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile != INVALID_HANDLE_VALUE ) {
CloseHandle(hFile);
return TRUE;
}
}
}
++DriveLetter;
}
return FALSE;
}
BOOL __cdecl LoadLevel(LPCTSTR fileName, int levelID) {
BOOL result = FALSE;
LPCTSTR fullPath;
HANDLE hFile;
DWORD reserved;
DWORD bytesRead;
int levelVersion;
fullPath = GetFullPath(fileName);
strcpy(LevelFileName, fullPath);
init_game_malloc();
hFile = CreateFile(fullPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN|FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE ) {
wsprintf(StringToShow, "LoadLevel(): Could not open %s (level %d)", fullPath, levelID);
return FALSE;
}
ReadFileSync(hFile, &levelVersion, sizeof(levelVersion), &bytesRead, NULL);
if( levelVersion != REQ_LEVEL_VERSION ) {
if( levelVersion < REQ_LEVEL_VERSION )
wsprintf(StringToShow, "FATAL: Level %d (%s) is OUT OF DATE (version %d). COPY NEW EDITOR", levelID, fullPath, fileName);
else
wsprintf(StringToShow, "FATAL: Level %d (%s) requires a new TOMB2.EXE (version %d) to run", levelID, fullPath, fileName);
goto EXIT;
}
#if (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.RenderMode == RM_Hardware ) {
LoadTexPagesConfiguration(LevelFileName);
}
#endif // (DIRECT3D_VERSION >= 0x900)
LevelFilePalettesOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
if( !LoadPalettes(hFile) ) {
goto EXIT;
}
LevelFileTexPagesOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
if( !LoadTexturePages(hFile) ) {
goto EXIT;
}
ReadFileSync(hFile, &reserved, sizeof(reserved), &bytesRead, NULL);
if( !LoadRooms(hFile) ||
!LoadObjects(hFile) ||
!LoadSprites(hFile) ||
!LoadCameras(hFile) ||
!LoadSoundEffects(hFile) ||
!LoadBoxes(hFile) ||
!LoadAnimatedTextures(hFile) ||
!LoadItems(hFile) )
{
goto EXIT;
}
LevelFileDepthQOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
if( !LoadDepthQ(hFile) ||
!LoadCinematic(hFile) ||
!LoadDemo(hFile) ||
!LoadSamples(hFile) )
{
goto EXIT;
}
LoadDemoExternal(fullPath);
#ifdef FEATURE_VIDEOFX_IMPROVED
MarkSemitransObjects();
MarkSemitransTextureRanges();
#endif // FEATURE_VIDEOFX_IMPROVED
#ifdef FEATURE_BACKGROUND_IMPROVED
PatternTexPage = CreateBgndPatternTexture(hFile);
#endif // FEATURE_BACKGROUND_IMPROVED
result = TRUE;
EXIT :
CloseHandle(hFile);
return result;
}
BOOL __cdecl S_LoadLevelFile(LPCTSTR fileName, int levelID, GF_LEVEL_TYPE levelType) {
S_UnloadLevelFile();
LoadLevelType = levelType; // NOTE: this line is not presented in the original game
#ifdef FEATURE_MOD_CONFIG
LoadModConfiguration(fileName);
BOOL result = LoadLevel(fileName, levelID);
#ifdef FEATURE_BACKGROUND_IMPROVED
if( LoadingScreensEnabled && GetModLoadingPix() && (levelType == GFL_NORMAL || levelType == GFL_SAVED) ) {
RGB888 palette[256];
memcpy(palette, GamePalette8, sizeof(GamePalette8));
if( !BGND2_LoadPicture(GetModLoadingPix(), FALSE, FALSE) ) {
BGND2_ShowPicture(30, 90, 10, 2, TRUE);
S_DontDisplayPicture();
InputStatus = 0;
}
memcpy(GamePalette8, palette, sizeof(GamePalette8));
}
#endif // FEATURE_BACKGROUND_IMPROVED
return result;
#else // FEATURE_MOD_CONFIG
return LoadLevel(fileName, levelID);
#endif // FEATURE_MOD_CONFIG
}
void __cdecl S_UnloadLevelFile() {
if( SavedAppSettings.RenderMode == RM_Hardware ) {
HWR_FreeTexturePages();
}
memset(TexturePageBuffer8, 0, sizeof(TexturePageBuffer8));
*LevelFileName = 0;
TextureInfoCount = 0;
#ifdef FEATURE_MOD_CONFIG
UnloadModConfiguration();
#endif // FEATURE_MOD_CONFIG
#if (DIRECT3D_VERSION >= 0x900)
UnloadTexPagesConfiguration();
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __cdecl S_AdjustTexelCoordinates() {
if( TextureInfoCount != 0 ) {
AdjustTextureUVs(false);
}
}
BOOL __cdecl S_ReloadLevelGraphics(BOOL reloadPalettes, BOOL reloadTexPages) {
HANDLE hFile;
if( *LevelFileName ) {
hFile = CreateFile(LevelFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE )
return FALSE;
#if (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.RenderMode == RM_Hardware ) {
LoadTexPagesConfiguration(LevelFileName);
}
#endif // (DIRECT3D_VERSION >= 0x900)
if( reloadPalettes && SavedAppSettings.RenderMode == RM_Software ) {
SetFilePointer(hFile, LevelFilePalettesOffset, NULL, FILE_BEGIN);
LoadPalettes(hFile);
SetFilePointer(hFile, LevelFileDepthQOffset, NULL, FILE_BEGIN);
LoadDepthQ(hFile);
}
if( reloadTexPages ) {
if( SavedAppSettings.RenderMode == RM_Hardware )
HWR_FreeTexturePages();
SetFilePointer(hFile, LevelFileTexPagesOffset, NULL, FILE_BEGIN);
LoadTexturePages(hFile);
#ifdef FEATURE_BACKGROUND_IMPROVED
PatternTexPage = CreateBgndPatternTexture(hFile);
#endif // FEATURE_BACKGROUND_IMPROVED
}
CloseHandle(hFile);
}
if( reloadPalettes )
InitColours();
return TRUE;
}
BOOL __cdecl Read_Strings(DWORD dwCount, char **stringTable, char **stringBuffer, LPDWORD lpBufferSize, HANDLE hFile) {
DWORD i, bytesRead;
UINT16 bufferSize;
UINT16 offsets[200]; // buffer for offsets
ReadFileSync(hFile, offsets, sizeof(UINT16) * dwCount, &bytesRead, NULL);
ReadFileSync(hFile, &bufferSize, sizeof(bufferSize), &bytesRead, NULL);
*lpBufferSize = bufferSize;
*stringBuffer = (char *)GlobalAlloc(GMEM_FIXED, bufferSize);
if( *stringBuffer == NULL )
return FALSE;
ReadFileSync(hFile, *stringBuffer, bufferSize, &bytesRead, NULL);
if( (GF_GameFlow.flags & GFF_UseSecurityTag) != 0 ) {
for( i = 0; i < bufferSize; ++i )
(*stringBuffer)[i] ^= GF_GameFlow.cypherCode;
}
for( i=0; i < dwCount; ++i ) {
stringTable[i] = &(*stringBuffer)[offsets[i]];
}
return TRUE;
}
BOOL __cdecl S_LoadGameFlow(LPCTSTR fileName) {
DWORD scriptVersion;
char scriptDescription[DESCRIPTION_LENGTH];
UINT16 offsets[200]; // buffer for offsets
DWORD bytesRead;
UINT16 flowSize, scriptSize, gameStringsCount;
BOOL result = FALSE;
LPCTSTR filePath = GetFullPath(fileName);
HANDLE hFile = CreateFile(filePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE )
return FALSE;
ReadFileSync(hFile, &scriptVersion, sizeof(DWORD), &bytesRead, NULL);
if( scriptVersion != REQ_SCRIPT_VERSION )
goto CLEANUP;
ReadFileSync(hFile, &scriptDescription, DESCRIPTION_LENGTH, &bytesRead, NULL);
ReadFileSync(hFile, &flowSize, sizeof(flowSize), &bytesRead, NULL);
if( flowSize != sizeof(GAME_FLOW) )
goto CLEANUP;
ReadFileSync(hFile, &GF_GameFlow, flowSize, &bytesRead, NULL);
READ_STRINGS(GF_GameFlow.num_Levels, GF_LevelNamesStringTable, &GF_LevelNamesStringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(GF_GameFlow.num_Pictures, GF_PictureFilesStringTable, &GF_PictureFilesStringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(GF_GameFlow.num_Titles, GF_TitleFilesStringTable, &GF_TitleFilesStringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(GF_GameFlow.num_Fmvs, GF_FmvFilesStringTable, &GF_FmvFilesStringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(GF_GameFlow.num_Levels, GF_LevelFilesStringTable, &GF_LevelFilesStringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(GF_GameFlow.num_Cutscenes, GF_CutsFilesStringTable, &GF_CutsFilesStringBuffer, &bytesRead, hFile, CLEANUP);
ReadFileSync(hFile, offsets, sizeof(UINT16) * (GF_GameFlow.num_Levels + 1), &bytesRead, NULL);
ReadFileSync(hFile, &scriptSize, sizeof(scriptSize), &bytesRead, NULL);
GF_ScriptBuffer = (__int16 *)GlobalAlloc(GMEM_FIXED, scriptSize);
if( GF_ScriptBuffer == NULL )
goto CLEANUP;
ReadFileSync(hFile, GF_ScriptBuffer, scriptSize, &bytesRead, NULL);
for( int i=0; i < (GF_GameFlow.num_Levels + 1); ++i ) {
GF_ScriptTable[i] = &GF_ScriptBuffer[offsets[i+1]/2];
}
if( GF_GameFlow.num_Demos > 0 )
ReadFileSync(hFile, GF_DemoLevels, sizeof(UINT16) * GF_GameFlow.num_Demos, &bytesRead, NULL);
ReadFileSync(hFile, &gameStringsCount, sizeof(gameStringsCount), &bytesRead, NULL);
if( gameStringsCount != REQ_GAME_STR_COUNT )
goto CLEANUP;
// game and platform specific strings
READ_STRINGS(gameStringsCount, GF_GameStringTable, &GF_GameStringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(SPECIFIC_STR_COUNT, GF_SpecificStringTable, &GF_SpecificStringBuffer, &bytesRead, hFile, CLEANUP);
// puzzle strings
READ_STRINGS(GF_GameFlow.num_Levels, GF_Puzzle1StringTable, &GF_Puzzle1StringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(GF_GameFlow.num_Levels, GF_Puzzle2StringTable, &GF_Puzzle2StringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(GF_GameFlow.num_Levels, GF_Puzzle3StringTable, &GF_Puzzle3StringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(GF_GameFlow.num_Levels, GF_Puzzle4StringTable, &GF_Puzzle4StringBuffer, &bytesRead, hFile, CLEANUP);
// pickup strings
READ_STRINGS(GF_GameFlow.num_Levels, GF_Pickup1StringTable, &GF_Pickup1StringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(GF_GameFlow.num_Levels, GF_Pickup2StringTable, &GF_Pickup2StringBuffer, &bytesRead, hFile, CLEANUP);
// key strings
READ_STRINGS(GF_GameFlow.num_Levels, GF_Key1StringTable, &GF_Key1StringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(GF_GameFlow.num_Levels, GF_Key2StringTable, &GF_Key2StringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(GF_GameFlow.num_Levels, GF_Key3StringTable, &GF_Key3StringBuffer, &bytesRead, hFile, CLEANUP);
READ_STRINGS(GF_GameFlow.num_Levels, GF_Key4StringTable, &GF_Key4StringBuffer, &bytesRead, hFile, CLEANUP);
result = TRUE;
CLEANUP:
CloseHandle(hFile);
return result;
}
/*
* Inject function
*/
void Inject_File() {
INJECT(0x00449980, ReadFileSync);
INJECT(0x004499D0, LoadTexturePages);
INJECT(0x00449B60, LoadRooms);
INJECT(0x00449F00, AdjustTextureUVs);
INJECT(0x00449FA0, LoadObjects);
INJECT(0x0044A520, LoadSprites);
INJECT(0x0044A660, LoadItems);
INJECT(0x0044A840, LoadDepthQ);
INJECT(0x0044A9D0, LoadPalettes);
INJECT(0x0044AA50, LoadCameras);
INJECT(0x0044AAB0, LoadSoundEffects);
INJECT(0x0044AB10, LoadBoxes);
INJECT(0x0044AD40, LoadAnimatedTextures);
INJECT(0x0044ADA0, LoadCinematic);
INJECT(0x0044AE20, LoadDemo);
INJECT(0x0044AEB0, LoadDemoExternal);
INJECT(0x0044AF50, LoadSamples);
INJECT(0x0044B1C0, ChangeFileNameExtension);
INJECT(0x0044B200, GetFullPath);
INJECT(0x0044B230, SelectDrive);
INJECT(0x0044B310, LoadLevel);
INJECT(0x0044B560, S_LoadLevelFile);
INJECT(0x0044B580, S_UnloadLevelFile);
INJECT(0x0044B5B0, S_AdjustTexelCoordinates);
INJECT(0x0044B5D0, S_ReloadLevelGraphics);
INJECT(0x0044B6A0, Read_Strings);
INJECT(0x0044B770, S_LoadGameFlow);
}
================================================
FILE: specific/file.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef FILE_H_INCLUDED
#define FILE_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
BOOL __cdecl ReadFileSync(HANDLE hFile, LPVOID lpBuffer, DWORD nBytesToRead, LPDWORD lpnBytesRead, LPOVERLAPPED lpOverlapped); // 0x00449980
BOOL __cdecl LoadTexturePages(HANDLE hFile); // 0x004499D0
BOOL __cdecl LoadRooms(HANDLE hFile); // 0x00449B60
void __cdecl AdjustTextureUVs(bool resetUvFix); // 0x00449F00
BOOL __cdecl LoadObjects(HANDLE hFile); // 0x00449FA0
BOOL __cdecl LoadSprites(HANDLE hFile); // 0x0044A520
BOOL __cdecl LoadItems(HANDLE hFile); // 0x0044A660
BOOL __cdecl LoadDepthQ(HANDLE hFile); // 0x0044A840
BOOL __cdecl LoadPalettes(HANDLE hFile); // 0x0044A9D0
BOOL __cdecl LoadCameras(HANDLE hFile); // 0x0044AA50
BOOL __cdecl LoadSoundEffects(HANDLE hFile); // 0x0044AAB0
BOOL __cdecl LoadBoxes(HANDLE hFile); // 0x0044AB10
BOOL __cdecl LoadAnimatedTextures(HANDLE hFile); // 0x0044AD40
BOOL __cdecl LoadCinematic(HANDLE hFile); // 0x0044ADA0
BOOL __cdecl LoadDemo(HANDLE hFile); // 0x0044AE20
void __cdecl LoadDemoExternal(LPCSTR levelName); // 0x0044AEB0
BOOL __cdecl LoadSamples(HANDLE hFile); // 0x0044AF50
void __cdecl ChangeFileNameExtension(char *fileName, const char *fileExt); // 0x0044B1C0
LPCTSTR __cdecl GetFullPath(LPCTSTR path); // 0x0044B200
BOOL __cdecl SelectDrive(); // 0x0044B230
BOOL __cdecl LoadLevel(LPCTSTR fileName, int levelID); // 0x0044B310
BOOL __cdecl S_LoadLevelFile(LPCTSTR fileName, int levelID, GF_LEVEL_TYPE levelType); // 0x0044B560
void __cdecl S_UnloadLevelFile(); // 0x0044B580
void __cdecl S_AdjustTexelCoordinates(); // 0x0044B5B0
BOOL __cdecl S_ReloadLevelGraphics(BOOL reloadPalettes, BOOL reloadTexPages); // 0x0044B5D0
BOOL __cdecl Read_Strings(DWORD dwCount, char **stringTable, char **stringBuffer, LPDWORD lpBufferSize, HANDLE hFile); // 0x0044B6A0
BOOL __cdecl S_LoadGameFlow(LPCTSTR fileName); // 0x0044B770
#endif // FILE_H_INCLUDED
================================================
FILE: specific/fmv.cpp
================================================
/*
* Copyright (c) 2017-2023 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/fmv.h"
#include "specific/file.h"
#include "specific/init_display.h"
#include "specific/input.h"
#include "specific/sndpc.h"
#include "global/vars.h"
// GetProcAddress Macro
#define GET_DLL_PROC(dll, proc) { \
*(FARPROC *)&(proc) = GetProcAddress((dll), #proc); \
if( proc == NULL ) throw #proc; \
}
#if (DIRECT3D_VERSION <= 0x500)
// The original FMV playback supports DirectX 5 only
// It does not support any newer version of DirectX
#define ESCAPE_DLL_NAME "winplay.dll"
static HMODULE hEscapePlay = NULL;
// Imports from winplay.dll
static int (__cdecl *Movie_GetCurrentFrame)(LPVOID);
static int (__cdecl *Movie_GetFormat)(LPVOID);
static int (__cdecl *Movie_GetSoundChannels)(LPVOID);
static int (__cdecl *Movie_GetSoundPrecision)(LPVOID);
static int (__cdecl *Movie_GetSoundRate)(LPVOID);
static int (__cdecl *Movie_GetTotalFrames)(LPVOID);
static int (__cdecl *Movie_GetXSize)(LPVOID);
static int (__cdecl *Movie_GetYSize)(LPVOID);
static int (__cdecl *Movie_SetSyncAdjust)(LPVOID, LPVOID, DWORD);
static int (__cdecl *Player_BlankScreen)(DWORD, DWORD, DWORD, DWORD);
static int (__cdecl *Player_GetDSErrorCode)();
static int (__cdecl *Player_InitMovie)(LPVOID, DWORD, DWORD, LPCTSTR, DWORD);
static int (__cdecl *Player_InitMoviePlayback)(LPVOID, LPVOID, LPVOID);
static int (__cdecl *Player_InitPlaybackMode)(HWND, LPVOID, DWORD, DWORD);
static int (__cdecl *Player_InitSound)(LPVOID, DWORD, DWORD, BOOL, DWORD, DWORD, DWORD, DWORD, DWORD);
static int (__cdecl *Player_InitSoundSystem)(HWND);
static int (__cdecl *Player_InitVideo)(LPVOID, LPVOID, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD);
static int (__cdecl *Player_PassInDirectDrawObject)(LPDIRECTDRAW2);
static int (__cdecl *Player_PlayFrame)(LPVOID, LPVOID, LPVOID, DWORD, LPRECT, DWORD, DWORD, DWORD);
static int (__cdecl *Player_ReturnPlaybackMode)(BOOL);
static int (__cdecl *Player_ShutDownMovie)(LPVOID);
static int (__cdecl *Player_ShutDownSound)(LPVOID);
static int (__cdecl *Player_ShutDownSoundSystem)();
static int (__cdecl *Player_ShutDownVideo)(LPVOID);
static int (__cdecl *Player_StartTimer)(LPVOID);
static int (__cdecl *Player_StopTimer)(LPVOID);
#endif // (DIRECT3D_VERSION <= 0x500)
#ifdef FEATURE_FFPLAY
#define FFPLAY_DLL_NAME "ffplay.dll"
static HMODULE hFFplay = NULL;
// Imports from ffplay.dll
static int (__stdcall *ffplay_init)(HWND, int, const char*);
static int (__stdcall *ffplay_play_video)(const char*, int, int, int, int);
static int (__stdcall *ffplay_cleanup)(void);
static const char videoExts[][4] = {
"MP4",
"BIK",
"RPL",
};
static bool FFplayInit() {
if( hFFplay != NULL ) {
return true;
}
hFFplay = LoadLibrary(FFPLAY_DLL_NAME);
if( hFFplay == NULL ) {
// failed to load DLL
return false;
}
try {
GET_DLL_PROC(hFFplay, ffplay_init);
GET_DLL_PROC(hFFplay, ffplay_play_video);
GET_DLL_PROC(hFFplay, ffplay_cleanup);
} catch (LPCTSTR procName) {
// failed to load one of the procs
FreeLibrary(hFFplay);
hFFplay = NULL;
return false;
}
if( 0 != ffplay_init(HGameWindow, 2, "winmm") ) {
// failed to init FFplay
FreeLibrary(hFFplay);
hFFplay = NULL;
return false;
}
return true;
}
void __cdecl FFplayCleanup() {
if( hFFplay != NULL ) {
ffplay_cleanup();
FreeLibrary(hFFplay);
hFFplay = NULL;
}
}
#endif // FEATURE_FFPLAY
bool __cdecl FMV_Init() {
#ifdef FEATURE_FFPLAY
if( FFplayInit() ) {
return true;
}
#endif // FEATURE_FFPLAY
#if (DIRECT3D_VERSION > 0x500)
return false;
#else // (DIRECT3D_VERSION > 0x500)
if( hEscapePlay != NULL ) {
return true;
}
hEscapePlay = LoadLibrary(ESCAPE_DLL_NAME);
if( hEscapePlay == NULL ) {
// failed to load DLL
return false;
}
try {
GET_DLL_PROC(hEscapePlay, Movie_GetCurrentFrame);
GET_DLL_PROC(hEscapePlay, Movie_GetFormat);
GET_DLL_PROC(hEscapePlay, Movie_GetSoundChannels);
GET_DLL_PROC(hEscapePlay, Movie_GetSoundPrecision);
GET_DLL_PROC(hEscapePlay, Movie_GetSoundRate);
GET_DLL_PROC(hEscapePlay, Movie_GetTotalFrames);
GET_DLL_PROC(hEscapePlay, Movie_GetXSize);
GET_DLL_PROC(hEscapePlay, Movie_GetYSize);
GET_DLL_PROC(hEscapePlay, Movie_SetSyncAdjust);
GET_DLL_PROC(hEscapePlay, Player_BlankScreen);
GET_DLL_PROC(hEscapePlay, Player_GetDSErrorCode);
GET_DLL_PROC(hEscapePlay, Player_InitMovie);
GET_DLL_PROC(hEscapePlay, Player_InitMoviePlayback);
GET_DLL_PROC(hEscapePlay, Player_InitPlaybackMode);
GET_DLL_PROC(hEscapePlay, Player_InitSound);
GET_DLL_PROC(hEscapePlay, Player_InitSoundSystem);
GET_DLL_PROC(hEscapePlay, Player_InitVideo);
GET_DLL_PROC(hEscapePlay, Player_PassInDirectDrawObject);
GET_DLL_PROC(hEscapePlay, Player_PlayFrame);
GET_DLL_PROC(hEscapePlay, Player_ReturnPlaybackMode);
GET_DLL_PROC(hEscapePlay, Player_ShutDownMovie);
GET_DLL_PROC(hEscapePlay, Player_ShutDownSound);
GET_DLL_PROC(hEscapePlay, Player_ShutDownSoundSystem);
GET_DLL_PROC(hEscapePlay, Player_ShutDownVideo);
GET_DLL_PROC(hEscapePlay, Player_StartTimer);
GET_DLL_PROC(hEscapePlay, Player_StopTimer);
} catch (LPCTSTR procName) {
// failed to load one of the procs
FreeLibrary(hEscapePlay);
hEscapePlay = NULL;
return false;
}
return true;
#endif // (DIRECT3D_VERSION > 0x500)
}
void __cdecl FMV_Cleanup() {
#ifdef FEATURE_FFPLAY
FFplayCleanup();
#endif // FEATURE_FFPLAY
#if (DIRECT3D_VERSION <= 0x500)
if( hEscapePlay != NULL ) {
FreeLibrary(hEscapePlay);
hEscapePlay = NULL;
}
#endif // (DIRECT3D_VERSION <= 0x500)
}
bool __cdecl PlayFMV(LPCTSTR fileName) {
LPCTSTR fullPath;
if( SavedAppSettings.DisableFMV )
return IsGameToExit;
S_CDStop();
ShowCursor(FALSE);
RenderFinish(true);
IsFmvPlaying = TRUE;
fullPath = GetFullPath(fileName);
WinPlayFMV(fullPath, true);
WinStopFMV(true);
IsFmvPlaying = FALSE;
if( !IsGameToExit )
FmvBackToGame();
ShowCursor(TRUE);
return IsGameToExit;
}
void __cdecl WinPlayFMV(LPCTSTR fileName, bool isPlayback) {
#ifdef FEATURE_FFPLAY
if( hFFplay != NULL ) {
char extFileName[256] = {0};
char *extension;
strncpy(extFileName, fileName, sizeof(extFileName)-1);
extension = PathFindExtension(extFileName);
if( extension == NULL ) {
extension = strchr(extFileName, 0);
*extension = '.';
}
for( unsigned int i = 0; i < sizeof(videoExts)/4; ++i ) {
memcpy(extension + 1, videoExts[i], 4);
if( INVALID_FILE_ATTRIBUTES != GetFileAttributes(extFileName) ) {
ffplay_play_video(extFileName, 0, 0, 0, 100);
return;
}
}
ffplay_play_video(fileName, 0, 0, 0, 100);
return;
}
#endif // FEATURE_FFPLAY
#if (DIRECT3D_VERSION <= 0x500)
int xSize, ySize, xOffset, yOffset;
int soundPrecision, soundRate, soundChannels, soundFormat;
bool isUncompressed;
RECT rect = {0, 0, 640, 480};
if( hEscapePlay == NULL ) {
return;
}
if( 0 != Player_PassInDirectDrawObject(DDraw) ||
0 != Player_InitMovie(&MovieContext, 0, 0, fileName, 0x200000) ||
130 != Movie_GetFormat(MovieContext) )
{
return;
}
xSize = Movie_GetXSize(MovieContext); // RPL movie width is always 320
ySize = Movie_GetYSize(MovieContext); // maximum possible value for RPL movie height is 240
xOffset = 320 - xSize;
yOffset = 240 - ySize;
if( 0 != Player_InitVideo(&FmvContext, MovieContext, xSize, ySize, xOffset, yOffset, 0, 0, 640, 480, 0, 1, 13) ||
(isPlayback && 0 != Player_InitPlaybackMode(HGameWindow, FmvContext, 1, 0)) )
{
return;
}
Player_BlankScreen(rect.left, rect.top, rect.right, rect.bottom);
if( 0 != Player_InitSoundSystem(HGameWindow) ||
Player_GetDSErrorCode() < 0 )
{
return;
}
soundPrecision = Movie_GetSoundPrecision(MovieContext);
soundRate = Movie_GetSoundRate(MovieContext);
soundChannels = Movie_GetSoundChannels(MovieContext);
isUncompressed = ( soundPrecision != 4 );
soundFormat = isUncompressed ? 1 : 4;
if( 0 != Player_InitSound(&FmvSoundContext, 16384, soundFormat, isUncompressed, 4096, soundChannels, soundRate, soundPrecision, 2) ) {
return;
}
Movie_SetSyncAdjust(MovieContext, FmvSoundContext, 4);
if( 0 != Player_InitMoviePlayback(MovieContext, FmvContext, FmvSoundContext) ) {
return;
}
S_UpdateInput(); // NOTE: should use WinVidSpinMessageLoop(false) instead
Player_StartTimer(MovieContext);
Player_BlankScreen(rect.left, rect.top, rect.right, rect.bottom);
S_UpdateInput(); // NOTE: should use WinVidSpinMessageLoop(false) instead
while( Movie_GetCurrentFrame(MovieContext) < Movie_GetTotalFrames(MovieContext) ) {
if( 0 != Player_PlayFrame(MovieContext, FmvContext, FmvSoundContext, 0, &rect, 0, 0, 0) )
return;
if( S_UpdateInput() || CHK_ANY(InputStatus, IN_OPTION) )
break;
}
#endif // (DIRECT3D_VERSION <= 0x500)
}
void __cdecl WinStopFMV(bool isPlayback) {
#if (DIRECT3D_VERSION <= 0x500)
if( hEscapePlay == NULL ) {
return;
}
Player_StopTimer(MovieContext);
Player_ShutDownSound(&FmvSoundContext);
Player_ShutDownVideo(&FmvContext);
Player_ShutDownMovie(&MovieContext);
Player_ShutDownSoundSystem();
if( isPlayback ) {
Player_ReturnPlaybackMode(isPlayback);
}
#endif // (DIRECT3D_VERSION <= 0x500)
}
bool __cdecl IntroFMV(LPCTSTR fileName1, LPCTSTR fileName2) {
LPCTSTR fullPath;
if( SavedAppSettings.DisableFMV )
return IsGameToExit;
ShowCursor(FALSE);
RenderFinish(true);
IsFmvPlaying = TRUE;
fullPath = GetFullPath(fileName1);
WinPlayFMV(fullPath, true);
WinStopFMV(true);
fullPath = GetFullPath(fileName2);
WinPlayFMV(fullPath, true);
WinStopFMV(true);
IsFmvPlaying = FALSE;
if( !IsGameToExit )
FmvBackToGame();
ShowCursor(TRUE);
return IsGameToExit;
}
/*
* Inject function
*/
void Inject_Fmv() {
INJECT(0x0044BE50, PlayFMV);
INJECT(0x0044BED0, WinPlayFMV);
INJECT(0x0044C1B0, WinStopFMV);
INJECT(0x0044C200, IntroFMV);
}
================================================
FILE: specific/fmv.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef FMV_H_INCLUDED
#define FMV_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
bool __cdecl FMV_Init();
void __cdecl FMV_Cleanup();
bool __cdecl PlayFMV(LPCTSTR fileName); // 0x0044BE50
void __cdecl WinPlayFMV(LPCSTR fileName, bool isPlayback); // 0x0044BED0
void __cdecl WinStopFMV(bool isPlayback); // 0x0044C1B0
bool __cdecl IntroFMV(LPCTSTR fileName1, LPCTSTR fileName2); // 0x0044C200
#endif // FMV_H_INCLUDED
================================================
FILE: specific/frontend.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/frontend.h"
#include "specific/display.h"
#include "specific/fmv.h"
#include "specific/input.h"
#include "specific/output.h"
#include "specific/texture.h"
#include "specific/utils.h"
#include "global/vars.h"
#ifdef FEATURE_BACKGROUND_IMPROVED
#include "modding/background_new.h"
bool IsFadeToBlack = false;
#endif // FEATURE_BACKGROUND_IMPROVED
#ifdef FEATURE_HUD_IMPROVED
#include "3dsystem/3dinsert.h"
DWORD InvTextBoxMode = 1;
#endif // FEATURE_HUD_IMPROVED
static void FadeWait() {
// Null function
}
UINT16 __cdecl S_COLOUR(int red, int green, int blue) {
return FindNearestPaletteEntry(GamePalette8, red, green, blue, false);
}
void __cdecl S_DrawScreenLine(int x, int y, int z, int xLen, int yLen, BYTE colorIdx, D3DCOLOR *gour, UINT16 flags) {
ins_line(x, y, x + xLen, y + yLen, PhdNearZ + z * 8, colorIdx);
}
void __cdecl S_DrawScreenBox(int sx, int sy, int z, int width, int height, BYTE colorIdx, GOURAUD_OUTLINE *gour, UINT16 flags) {
int adder;
#ifdef FEATURE_HUD_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode ) {
adder = GetRenderScale(1);
sx += adder;
sy += adder;
} else {
adder = GetRenderScale(2);
}
#else // !FEATURE_HUD_IMPROVED
// NOTE: in the original code there was no adder at all for this function
adder = 2;
#endif // FEATURE_HUD_IMPROVED
width += adder;
height += adder;
#ifdef FEATURE_HUD_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode && gour != NULL ) {
int x[3], y[3], lw;
int sz = PhdNearZ + z * 8;
lw = GetRenderScale(1);
x[0] = sx;
y[0] = sy;
x[2] = sx + width;
y[2] = sy + height;
x[1] = (x[0] + x[2]) / 2;
y[1] = (y[0] + y[2]) / 2;
InsertGourQuad(x[0]-lw, y[0]-lw, x[1], y[0], sz,
gour->clr[0], gour->clr[1],
gour->clr[1], gour->clr[0]);
InsertGourQuad(x[1], y[0]-lw, x[2], y[0], sz,
gour->clr[1], gour->clr[2],
gour->clr[2], gour->clr[1]);
InsertGourQuad(x[2], y[0]-lw, x[2]+lw, y[1], sz,
gour->clr[2], gour->clr[2],
gour->clr[3], gour->clr[3]);
InsertGourQuad(x[2], y[1], x[2]+lw, y[2], sz,
gour->clr[3], gour->clr[3],
gour->clr[4], gour->clr[4]);
InsertGourQuad(x[1], y[2], x[2]+lw, y[2]+lw, sz,
gour->clr[5], gour->clr[4],
gour->clr[4], gour->clr[5]);
InsertGourQuad(x[0], y[2], x[1], y[2]+lw, sz,
gour->clr[6], gour->clr[5],
gour->clr[5], gour->clr[6]);
InsertGourQuad(x[0]-lw, y[1], x[0], y[2]+lw, sz,
gour->clr[7], gour->clr[7],
gour->clr[6], gour->clr[6]);
InsertGourQuad(x[0]-lw, y[0], x[0], y[1], sz,
gour->clr[8], gour->clr[8],
gour->clr[7], gour->clr[7]);
return;
}
#endif // FEATURE_HUD_IMPROVED
const BYTE colorIdx1 = 15;
const BYTE colorIdx2 = 31;
int sx1 = sx + width;
int sy1 = sy + height;
#ifdef FEATURE_HUD_IMPROVED
int pixel = GetRenderScale(1);
ins_flat_rect(sx+pixel*0, sy-pixel*1, sx1+pixel*2, sy-pixel*0, z, colorIdx1);
ins_flat_rect(sx+pixel*1, sy-pixel*0, sx1+pixel*1, sy+pixel*1, z, colorIdx2);
ins_flat_rect(sx1+pixel*0, sy+pixel*1, sx1+pixel*1, sy1+pixel*1, z, colorIdx1);
ins_flat_rect(sx1+pixel*1, sy+pixel*0, sx1+pixel*2, sy1+pixel*2, z, colorIdx2);
ins_flat_rect(sx-pixel*1, sy-pixel*1, sx+pixel*0, sy1+pixel*1, z, colorIdx1);
ins_flat_rect(sx-pixel*0, sy-pixel*0, sx+pixel*1, sy1+pixel*0, z, colorIdx2);
ins_flat_rect(sx-pixel*0, sy1+pixel*0, sx1+pixel*0, sy1+pixel*1, z, colorIdx1);
ins_flat_rect(sx-pixel*1, sy1+pixel*1, sx1+pixel*1, sy1+pixel*2, z, colorIdx2);
#else // !FEATURE_HUD_IMPROVED
// NOTE: line coordinates slightly adjusted to fill gaps in the box corners (original game HWR bug)
if( SavedAppSettings.RenderMode == RM_Hardware ) {
++width;
++height;
}
S_DrawScreenLine(sx, sy-1, z, width+1, 0, colorIdx1, NULL, flags);
S_DrawScreenLine(sx+1, sy, z, width-1, 0, colorIdx2, NULL, flags);
S_DrawScreenLine(sx1, sy+1, z, 0, height-1, colorIdx1, NULL, flags);
S_DrawScreenLine(sx1+1, sy, z, 0, height+1, colorIdx2, NULL, flags);
S_DrawScreenLine(sx-1, sy-1, z, 0, height+1, colorIdx1, NULL, flags);
S_DrawScreenLine(sx, sy, z, 0, height-1, colorIdx2, NULL, flags);
S_DrawScreenLine(sx, sy1, z, width-1, 0, colorIdx1, NULL, flags);
S_DrawScreenLine(sx-1, sy1+1, z, width+1, 0, colorIdx2, NULL, flags);
#endif // !FEATURE_HUD_IMPROVED
}
void __cdecl S_DrawScreenFBox(int sx, int sy, int z, int width, int height, BYTE colorIdx, GOURAUD_FILL *gour, UINT16 flags) {
int adder;
int sz = PhdNearZ + z * 8;
#ifdef FEATURE_HUD_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode ) {
adder = GetRenderScale(1);
sx += adder;
sy += adder;
} else {
adder = GetRenderScale(2);
}
#else // !FEATURE_HUD_IMPROVED
// NOTE: in the original code the adder was 1, but 1 is insufficient,
// because there was visible gap between FBox and bottom/right Frame
adder = 2;
#endif // FEATURE_HUD_IMPROVED
width += adder;
height += adder;
#ifdef FEATURE_HUD_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode && gour != NULL ) {
int x[3], y[3];
x[0] = sx;
y[0] = sy;
x[2] = sx + width;
y[2] = sy + height;
x[1] = (x[0] + x[2]) / 2;
y[1] = (y[0] + y[2]) / 2;
InsertGourQuad(x[0], y[0], x[1], y[1], sz,
gour->clr[0][0], gour->clr[0][1],
gour->clr[0][2], gour->clr[0][3]);
InsertGourQuad(x[1], y[0], x[2], y[1], sz,
gour->clr[1][0], gour->clr[1][1],
gour->clr[1][2], gour->clr[1][3]);
InsertGourQuad(x[1], y[1], x[2], y[2], sz,
gour->clr[2][0], gour->clr[2][1],
gour->clr[2][2], gour->clr[2][3]);
InsertGourQuad(x[0], y[1], x[1], y[2], sz,
gour->clr[3][0], gour->clr[3][1],
gour->clr[3][2], gour->clr[3][3]);
return;
}
#endif // FEATURE_HUD_IMPROVED
ins_trans_quad(sx, sy, width, height, sz);
}
void __cdecl S_FinishInventory() {
if( InventoryMode != INV_TitleMode )
TempVideoRemove();
}
void __cdecl S_FadeToBlack() {
#ifdef FEATURE_BACKGROUND_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware ) {
IsFadeToBlack = true;
S_CopyScreenToBuffer();
BGND2_ShowPicture(0, 0, 10, 2, FALSE);
IsFadeToBlack = false;
return;
}
#endif // FEATURE_BACKGROUND_IMPROVED
memset(GamePalette8, 0, sizeof(GamePalette8));
FadeToPal(10, GamePalette8);
FadeWait();
// make two blank frames
#if (DIRECT3D_VERSION >= 0x900)
ScreenClear(false);
S_Wait(2 * TICKS_PER_FRAME, FALSE);
#else // (DIRECT3D_VERSION >= 0x900)
ScreenClear(false); ScreenDump();
ScreenClear(false); ScreenDump();
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __cdecl S_Wait(int timeout, BOOL inputCheck) {
// Wait for key event to clear or timeout
for( ; timeout > 0; --timeout ) {
if( !inputCheck || InputStatus == 0 )
break;
S_UpdateInput();
if( IsGameToExit ) return; // NOTE: this line is not in the original game
SyncTicks(1); // NOTE: there was another code in the original game
}
// Wait for key event to set or timeout
for( ; timeout > 0; --timeout ) {
S_UpdateInput();
if( IsGameToExit ) return; // NOTE: this line is not in the original game
if( inputCheck && InputStatus != 0 )
break;
SyncTicks(1); // NOTE: there was another code in the original game
}
}
bool __cdecl S_PlayFMV(LPCTSTR fileName) {
return PlayFMV(fileName);
}
bool __cdecl S_IntroFMV(LPCTSTR fileName1, LPCTSTR fileName2) {
return IntroFMV(fileName1, fileName2);
}
/*
* Inject function
*/
void Inject_Frontend() {
INJECT(0x0044C2A0, S_COLOUR);
INJECT(0x0044C2D0, S_DrawScreenLine);
INJECT(0x0044C310, S_DrawScreenBox);
INJECT(0x0044C430, S_DrawScreenFBox);
INJECT(0x0044C460, S_FinishInventory);
INJECT(0x0044C470, S_FadeToBlack);
INJECT(0x0044C4C0, S_Wait);
INJECT(0x0044C520, S_PlayFMV);
INJECT(0x0044C530, S_IntroFMV);
}
================================================
FILE: specific/frontend.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef FRONTEND_H_INCLUDED
#define FRONTEND_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
UINT16 __cdecl S_COLOUR(int red, int green, int blue); // 0x0044C2A0
void __cdecl S_DrawScreenLine(int x, int y, int z, int xLen, int yLen, BYTE colorIdx, D3DCOLOR *gour, UINT16 flags); // 0x0044C2D0
void __cdecl S_DrawScreenBox(int sx, int sy, int z, int width, int height, BYTE colorIdx, GOURAUD_OUTLINE *gour, UINT16 flags); // 0x0044C310
void __cdecl S_DrawScreenFBox(int sx, int sy, int z, int width, int height, BYTE colorIdx, GOURAUD_FILL *gour, UINT16 flags); // 0x0044C430
void __cdecl S_FinishInventory(); // 0x0044C460
void __cdecl S_FadeToBlack(); // 0x0044C470
void __cdecl S_Wait(int timeout, BOOL inputCheck); // 0x0044C4C0
bool __cdecl S_PlayFMV(LPCTSTR fileName); // 0x0044C520
bool __cdecl S_IntroFMV(LPCTSTR fileName1, LPCTSTR fileName2); // 0x0044C530
#endif // FRONTEND_H_INCLUDED
================================================
FILE: specific/game.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/game.h"
#include "game/camera.h"
#include "game/control.h"
#include "game/draw.h"
#include "game/inventory.h"
#include "game/invtext.h"
#include "game/savegame.h"
#include "game/setup.h"
#include "game/text.h"
#include "specific/background.h"
#include "specific/display.h"
#include "specific/file.h"
#include "specific/frontend.h"
#include "specific/init.h"
#include "specific/input.h"
#include "specific/output.h"
#include "specific/sndpc.h"
#include "specific/winvid.h"
#include "specific/texture.h"
#include "global/vars.h"
#ifdef FEATURE_BACKGROUND_IMPROVED
#include "modding/background_new.h"
extern DWORD StatsBackgroundMode;
#endif // FEATURE_BACKGROUND_IMPROVED
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/joy_output.h"
#endif // FEATURE_INPUT_IMPROVED
#ifdef FEATURE_GOLD
extern bool IsGold();
#endif // FEATURE_GOLD
#ifdef FEATURE_VIDEOFX_IMPROVED
extern void ResetGoldenLaraAlpha();
#endif // FEATURE_VIDEOFX_IMPROVED
#ifdef FEATURE_HUD_IMPROVED
extern bool GF_IsFinalLevel(DWORD levelID);
extern void RemoveJoystickHintText(bool isSelect, bool isContinue, bool isDeselect);
extern void DisplayJoystickHintText(bool isSelect, bool isContinue, bool isDeselect);
#endif // FEATURE_HUD_IMPROVED
#ifdef FEATURE_SUBFOLDERS
#include "modding/file_utils.h"
static int GetSaveFileName(LPSTR destName, DWORD destSize, int slotNumber) {
if( destName == NULL || destSize == 0 || slotNumber < 0 ) {
return -1;
}
#ifdef FEATURE_GOLD
snprintf(destName, destSize, ".\\saves%s\\savegame.%d", IsGold()?"Gold":"", slotNumber);
#else // !FEATURE_GOLD
snprintf(destName, destSize, ".\\saves\\savegame.%d", slotNumber);
#endif // !FEATURE_GOLD
return 0;
}
#endif // FEATURE_SUBFOLDERS
DWORD SavegameSlots = 16;
__int16 __cdecl StartGame(int levelID, GF_LEVEL_TYPE levelType) {
if( levelType == GFL_NORMAL || levelType == GFL_SAVED || levelType == GFL_DEMO )
CurrentLevel = levelID;
if( levelType != GFL_SAVED )
ModifyStartInfo(levelID);
IsTitleLoaded = FALSE;
if( levelType != GFL_SAVED )
InitialiseLevelFlags();
if( !InitialiseLevel(levelID, levelType) ) {
CurrentLevel = 0;
return GF_EXIT_GAME;
}
#ifdef FEATURE_VIDEOFX_IMPROVED
ResetGoldenLaraAlpha();
#endif // FEATURE_VIDEOFX_IMPROVED
int res = GameLoop(FALSE);
#ifdef FEATURE_INPUT_IMPROVED
JoyOutputReset();
#endif // FEATURE_INPUT_IMPROVED
switch( res ) {
case GF_EXIT_GAME :
CurrentLevel = 0;
return GF_EXIT_GAME;
case GF_START_DEMO :
case GF_EXIT_TO_TITLE :
return res;
}
if( IsLevelComplete ) {
if( (GF_GameFlow.flags & GFF_DemoVersion) != 0 && GF_GameFlow.singleLevel )
return GF_EXIT_TO_TITLE;
if( CurrentLevel == 0 ) { // Assault (Lara Home)
S_FadeToBlack();
return GF_EXIT_TO_TITLE;
} else { // Normal level
S_FadeInInventory(TRUE);
return GF_LEVEL_COMPLETE | CurrentLevel;
}
}
S_FadeToBlack();
if( !InventoryChosen )
return GF_EXIT_TO_TITLE;
if( InventoryExtraData[0] == 0 ) { // passport page (Load game)
S_LoadGame(&SaveGame, sizeof(SAVEGAME_INFO), InventoryExtraData[1]);
return GF_START_SAVEDGAME | InventoryExtraData[1]; // saveGame slot
}
if( InventoryExtraData[0] == 1 ) { // passport page (New game | Restart Level)
#ifdef FEATURE_HUD_IMPROVED
if( InventoryMode == INV_DeathMode ) {
// check if previous level is not Assault and not a final one (before bonus level)
if( CurrentLevel > 1 && !GF_IsFinalLevel(CurrentLevel-1) ) {
SaveGame.start[CurrentLevel] = SaveGame.start[CurrentLevel-1];
} else {
ModifyStartInfo(CurrentLevel);
}
return GF_START_GAME | CurrentLevel;
}
#endif // FEATURE_HUD_IMPROVED
if( (GF_GameFlow.flags & GFF_SelectAnyLevel) != 0 )
return GF_START_GAME | (InventoryExtraData[1] + 1); // selected level
else
return GF_START_GAME | 1; // first level
}
return GF_EXIT_TO_TITLE;
}
int __cdecl GameLoop(BOOL demoMode) {
int result;
int nTicks;
OverlayStatus = 1;
InitialiseCamera();
NoInputCounter = 0;
result = ControlPhase(1, demoMode);
while( result == 0 ) {
nTicks = DrawPhaseGame();
result = IsGameToExit ? GF_EXIT_GAME : ControlPhase(nTicks, demoMode);
}
S_SoundStopAllSamples();
#ifdef FEATURE_BACKGROUND_IMPROVED
// this fixes issue when the final "bath" cut scene is cut off
if( Lara.extra_anim && LaraItem->currentAnimState == EXTRA_FINALANIM ) {
S_CopyScreenToBuffer();
BGND2_ShowPicture(0, 0, 20, 70, FALSE);
}
#endif // FEATURE_BACKGROUND_IMPROVED
S_CDStop();
if( MusicVolume > 0 ) {
S_CDVolume(MusicVolume * 25 + 5);
}
return result;
}
int __cdecl LevelCompleteSequence() {
return GF_EXIT_TO_TITLE;
}
int __cdecl LevelStats(int levelID) {
int hours, minutes, seconds;
char timeString[100] = {0};
CreateStartInfo(levelID); // NOTE: this line is absent in the original code, but it's required for "Restart Level" feature
SaveGame.start[levelID].statistics = SaveGame.statistics;
seconds = SaveGame.statistics.timer / 30 % 60;
minutes = SaveGame.statistics.timer / 30 / 60 % 60;
hours = SaveGame.statistics.timer / 30 / 60 / 60;
sprintf(timeString, "%02d:%02d:%02d", hours, minutes, seconds);
#ifdef FEATURE_BACKGROUND_IMPROVED
TempVideoAdjust(HiRes, 1.0);
if( SavedAppSettings.RenderMode == RM_Software ) {
S_CopyScreenToBuffer();
RGB888 gamePal[256];
memcpy(gamePal, GamePalette8, sizeof(gamePal));
for( int i = 0; i<256; ++i ) {
GamePalette8[i] = gamePal[DepthQIndex[i]];
}
FadeToPal(10, GamePalette8);
memcpy(GamePalette8, gamePal, sizeof(gamePal));
FadeToPal(0, GamePalette8);
} else if( !StatsBackgroundMode ) {
S_CopyScreenToBuffer();
while( !IsGameToExit && BGND2_FadeTo(128, 0) > 128 ) {
S_InitialisePolyList(FALSE);
S_CopyBufferToScreen();
if( S_UpdateInput() || IsResetFlag ) {
break;
}
S_OutputPolyList();
S_DumpScreen();
}
} else {
S_FadeToBlack();
}
T_InitPrint();
S_CDPlay(GF_GameFlow.levelCompleteTrack, FALSE);
#else // // FEATURE_BACKGROUND_IMPROVED
S_CDPlay(GF_GameFlow.levelCompleteTrack, FALSE);
TempVideoAdjust(HiRes, 1.0);
FadeToPal(30, GamePalette8);
T_InitPrint();
S_CopyScreenToBuffer();
#endif // FEATURE_BACKGROUND_IMPROVED
while( CHK_ANY(InputStatus, IN_SELECT) )
S_UpdateInput();
#ifdef FEATURE_HUD_IMPROVED
DisplayJoystickHintText(false, true, false);
#endif // FEATURE_HUD_IMPROVED
while( !CHK_ANY(InputStatus, IN_SELECT) ) {
S_InitialisePolyList(FALSE);
S_CopyBufferToScreen();
S_UpdateInput();
// NOTE: this check is absent in the original game
if( IsGameToExit ) {
break;
}
if( IsResetFlag ) {
InputStatus = IN_SELECT;
}
InputDB = GetDebouncedInput(InputStatus);
ShowStatsText(timeString, FALSE);
T_DrawText();
S_OutputPolyList();
S_DumpScreen();
}
#ifdef FEATURE_HUD_IMPROVED
RemoveJoystickHintText(false, true, false);
#endif // FEATURE_HUD_IMPROVED
// NOTE: This LevelStats bonusFlag activation is not presented in the original game.
// If the level is final, but there no GFE_GAMECOMPLETE in the script, just activate Game+ here.
if( levelID == GF_GameFlow.num_Levels-GF_GameFlow.num_Demos-1 ) {
SaveGame.bonusFlag = true;
}
++levelID;
CreateStartInfo(levelID);
SaveGame.currentLevel = levelID;
SaveGame.start[CurrentLevel].available = 0; // make level not available
S_FadeToBlack();
TempVideoRemove();
return 0;
}
int __cdecl GameStats(int levelID) {
SaveGame.start[CurrentLevel].statistics = SaveGame.statistics;
TempVideoAdjust(HiRes, 1.0); // NOTE: this line was not in the original code
T_InitPrint();
while( CHK_ANY(InputStatus, IN_SELECT) )
S_UpdateInput();
#ifdef FEATURE_HUD_IMPROVED
DisplayJoystickHintText(false, true, false);
#endif // FEATURE_HUD_IMPROVED
while( !CHK_ANY(InputStatus, IN_SELECT) ) {
S_InitialisePolyList(FALSE);
S_CopyBufferToScreen();
S_UpdateInput();
// NOTE: this check is absent in the original game
if( IsGameToExit ) {
break;
}
if( IsResetFlag ) {
InputStatus = IN_SELECT;
}
InputDB = GetDebouncedInput(InputStatus);
ShowEndStatsText();
T_DrawText();
S_OutputPolyList();
S_DumpScreen();
}
#ifdef FEATURE_HUD_IMPROVED
RemoveJoystickHintText(false, true, false);
#endif // FEATURE_HUD_IMPROVED
// NOTE: in the original game, there is slightly different bonusFlag activation.
// Here removed bonuses initialization, and added the check that the level is final
if( CurrentLevel == GF_GameFlow.num_Levels-GF_GameFlow.num_Demos-1 ) {
SaveGame.bonusFlag = true;
}
#ifdef FEATURE_BACKGROUND_IMPROVED
BGND2_ShowPicture(0, 0, 10, 2, FALSE);
#endif // FEATURE_BACKGROUND_IMPROVED
S_DontDisplayPicture();
TempVideoRemove(); // NOTE: this line was not in the original code
return 0;
}
int __cdecl GetRandomControl() {
RandomControl = RandomControl * 1103515245 + 12345;
return (RandomControl >> 10) & 0x7FFF;
// NOTE: the shift value should be 0x10, but the original game has 10,
// it left "as is" to save consistency with the original game.
}
void __cdecl SeedRandomControl(int seed) {
RandomControl = seed;
}
int __cdecl GetRandomDraw() {
RandomDraw = RandomDraw * 1103515245 + 12345;
return (RandomDraw >> 10) & 0x7FFF;
// NOTE: the shift value should be 0x10, but the original game has 10,
// it left "as is" to save consistency with the original game.
}
void __cdecl SeedRandomDraw(int seed) {
RandomDraw = seed;
}
void __cdecl GetValidLevelsList(REQUEST_INFO *req) {
RemoveAllReqItems(req);
// NOTE: this check fixes original game bug.
// Now demo levels are excluded from the *"New Game"* menu,
// if the *"Select Level"* option is active.
DWORD numLevels = GF_GameFlow.num_Levels - GF_GameFlow.num_Demos;
for( DWORD i = 1; i < numLevels; ++i )
AddRequesterItem(req, GF_LevelNamesStringTable[i], 0, NULL, 0);
}
void __cdecl GetSavedGamesList(REQUEST_INFO *req) {
extern void SetPassportRequesterSize(REQUEST_INFO *req);
SetPassportRequesterSize(req);
if( req->selected >= req->visibleCount ) {
req->lineOffset = req->selected - req->visibleCount + 1;
}
memcpy(RequesterItemFlags1, SaveGameItemFlags1, sizeof(RequesterItemFlags1));
memcpy(RequesterItemFlags2, SaveGameItemFlags2, sizeof(RequesterItemFlags2));
}
void __cdecl DisplayCredits() {
int i;
RGB888 palette[256];
#ifdef FEATURE_BACKGROUND_IMPROVED
char fileName[64] = {0};
#else // !FEATURE_BACKGROUND_IMPROVED
DWORD bytesRead;
HANDLE hFile;
#ifdef FEATURE_GOLD
DWORD fileSize[10];
BYTE *fileData[10];
#else // !FEATURE_GOLD
DWORD fileSize[9];
BYTE *fileData[9];
#endif // !FEATURE_GOLD
DWORD bitmapSize;
BYTE *bitmapData;
LPCSTR fullPath;
char fileName[64] = "data\\credit0?.pcx";
#endif // FEATURE_BACKGROUND_IMPROVED
S_FadeToBlack(); // fade out 12 frames / 0.4 seconds
S_UnloadLevelFile();
TempVideoAdjust(HiRes, 1.0); // NOTE: this line was not in the original code
if( !InitialiseLevel(0, 0) ) // init title level
return;
memcpy(palette, GamePalette8, sizeof(GamePalette8));
memset(GamePalette8, 0, sizeof(GamePalette8));
IsVidModeLock = true;
FadeToPal(0, GamePalette8); // fade in instantly
#ifdef FEATURE_BACKGROUND_IMPROVED
S_CDPlay(52, FALSE);
// slideshow loop
#ifdef FEATURE_GOLD
for( i=IsGold()?0:1; i<100; ++i ) {
#else // !FEATURE_GOLD
for( i=1; i<100; ++i ) {
#endif // !FEATURE_GOLD
snprintf(fileName, sizeof(fileName), "data\\credit%02d.pcx", i);
if( !BGND2_LoadPicture(fileName, FALSE, FALSE) ) {
BGND2_ShowPicture(30, 225, 10, 2, FALSE);
}
S_DontDisplayPicture();
if( IsGameToExit ) {
return;
}
}
memcpy(GamePalette8, palette, sizeof(GamePalette8));
// NOTE: background for final statistics (picture is presented but not used in the original game)
if( !BGND2_LoadPicture("data\\end.pcx", TRUE, FALSE) ) {
BGND2_ShowPicture(30, 0, 0, 0, FALSE);
}
IsVidModeLock = false;
TempVideoRemove();
#else // !FEATURE_BACKGROUND_IMPROVED
// credit files load loop (preload all files may be reasonable because of CDAudio issues, since PCX are on CD too)
#ifdef FEATURE_GOLD
for( i=0; i<(IsGold()?10:9); ++i ) {
fileName[12] = '0'+i+(IsGold()?0:1);
#else // !FEATURE_GOLD
for( i=0; i<9; ++i ) {
fileName[12] = '0'+i+1;
#endif // !FEATURE_GOLD
fullPath = GetFullPath(fileName);
hFile = CreateFile(fullPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile != INVALID_HANDLE_VALUE ) {
fileSize[i] = GetFileSize(hFile, 0);
fileData[i] = (BYTE *)game_malloc(fileSize[i], GBUF_LoadPiccyBuffer);
ReadFile(hFile, fileData[i], fileSize[i], &bytesRead, 0);
CloseHandle(hFile);
}
}
bitmapSize = 640*480;
bitmapData = (BYTE *)game_malloc(bitmapSize, GBUF_LoadPiccyBuffer);
S_CDPlay(52, FALSE);
// slideshow loop
#ifdef FEATURE_GOLD
for( i=0; i<(IsGold()?10:9); ++i ) {
#else // !FEATURE_GOLD
for( i=0; i<9; ++i ) {
#endif // !FEATURE_GOLD
DecompPCX(fileData[i], fileSize[i], bitmapData, PicPalette);
if( SavedAppSettings.RenderMode == RM_Software ) {
#if (DIRECT3D_VERSION >= 0x900)
if( PictureBuffer.bitmap != NULL)
memcpy(PictureBuffer.bitmap, bitmapData, PictureBuffer.width * PictureBuffer.height);
#else // (DIRECT3D_VERSION >= 0x900)
WinVidCopyBitmapToBuffer(PictureBufferSurface, bitmapData);
#endif // (DIRECT3D_VERSION >= 0x900)
} else {
BGND_Make640x480(bitmapData, PicPalette);
}
#if (DIRECT3D_VERSION >= 0x900)
memcpy(GamePalette8, PicPalette, sizeof(GamePalette8));
#else // (DIRECT3D_VERSION >= 0x900)
CopyBitmapPalette(PicPalette, bitmapData, bitmapSize, GamePalette8);
#endif // (DIRECT3D_VERSION >= 0x900)
S_InitialisePolyList(FALSE);
S_CopyBufferToScreen();
S_OutputPolyList();
S_DumpScreen();
FadeToPal(30, GamePalette8); // fade in 30 frames / 1.0 seconds (software renderer only)
S_Wait(225 * TICKS_PER_FRAME, FALSE); // wait 225 frames / 7.5 seconds (disable keyboard)
S_FadeToBlack(); // fade out 12 frames / 0.4 seconds (software renderer only)
S_DontDisplayPicture();
if( IsGameToExit )
break;
}
memcpy(GamePalette8, palette, sizeof(GamePalette8));
S_Wait(150 * TICKS_PER_FRAME, FALSE); // wait 150 frames / 5 seconds (disable keyboard)
FadeToPal(30, GamePalette8); // fade in 30 frames / 1.0 seconds (software renderer only)
IsVidModeLock = false;
TempVideoRemove(); // NOTE: this line was not in the original code
// NOTE: here is no game_free for game_malloc. Memory will be free in S_DisplayPicture by init_game_malloc call
#endif // FEATURE_BACKGROUND_IMPROVED
}
BOOL __cdecl S_FrontEndCheck() {
HANDLE hFile;
DWORD bytesRead;
DWORD saveCounter;
char levelName[80] = {0};
char saveCountStr[16] = {0};
#ifdef FEATURE_SUBFOLDERS
char fileName[256] = {0};
#else // !FEATURE_SUBFOLDERS
char fileName[16] = {0};
#endif // !FEATURE_SUBFOLDERS
Init_Requester(&LoadGameRequester);
SavedGamesCount = 0;
for( DWORD i=0; i SaveCounter ) {
SaveCounter = saveCounter;
LoadGameRequester.selected = i;
}
SaveSlotFlags[i] = 1;
++SavedGamesCount;
}
memcpy(SaveGameItemFlags1, RequesterItemFlags1, sizeof(SaveGameItemFlags1));
memcpy(SaveGameItemFlags2, RequesterItemFlags2, sizeof(SaveGameItemFlags2));
++SaveCounter;
return 1;
}
BOOL __cdecl S_SaveGame(LPCVOID saveData, DWORD saveSize, int slotNumber) {
HANDLE hFile;
DWORD bytesWritten;
char levelName[80] = {0};
char saveCountStr[16] = {0};
#ifdef FEATURE_SUBFOLDERS
char fileName[256] = {0};
GetSaveFileName(fileName, sizeof(fileName), slotNumber);
if( CreateDirectories(fileName, true) ) {
return FALSE;
}
#else // !FEATURE_SUBFOLDERS
char fileName[16] = {0};
wsprintf(fileName, "savegame.%d", slotNumber);
#endif // !FEATURE_SUBFOLDERS
hFile = CreateFile(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE )
return FALSE;
wsprintf(levelName, "%s", GF_LevelNamesStringTable[SaveGame.currentLevel]);
WriteFile(hFile, levelName, 75, &bytesWritten, NULL);
WriteFile(hFile, &SaveCounter, sizeof(DWORD), &bytesWritten, NULL);
WriteFile(hFile, saveData, saveSize, &bytesWritten, NULL);
CloseHandle(hFile);
wsprintf(saveCountStr, "%d", SaveCounter);
ChangeRequesterItem(&LoadGameRequester, slotNumber, levelName, REQFLAG_LEFT, saveCountStr, REQFLAG_RIGHT);
// NOTE: the next two lines fix a bug in the original game:
// When player saves the game to EMPTY SLOT, save counter won't appear until the game relaunch
SaveGameItemFlags1[slotNumber] = RequesterItemFlags1[slotNumber];
SaveGameItemFlags2[slotNumber] = RequesterItemFlags2[slotNumber];
++SaveCounter;
// NOTE: There was no such check in the original code. Save files counter incremented anyway
if( SaveSlotFlags[slotNumber] == 0 ) {
SaveSlotFlags[slotNumber] = 1;
++SavedGamesCount;
}
return TRUE;
}
BOOL __cdecl S_LoadGame(LPVOID saveData, DWORD saveSize, int slotNumber) {
HANDLE hFile;
DWORD bytesRead;
DWORD saveCounter;
char levelName[80] = {0};
#ifdef FEATURE_SUBFOLDERS
char fileName[256] = {0};
GetSaveFileName(fileName, sizeof(fileName), slotNumber);
#else // !FEATURE_SUBFOLDERS
char fileName[16] = {0};
wsprintf(fileName, "savegame.%d", slotNumber);
#endif // !FEATURE_SUBFOLDERS
hFile = CreateFile(fileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE )
return FALSE;
ReadFile(hFile, levelName, 75, &bytesRead, NULL);
ReadFile(hFile, &saveCounter, sizeof(DWORD), &bytesRead, NULL);
ReadFile(hFile, saveData, saveSize, &bytesRead, NULL);
CloseHandle(hFile);
return TRUE;
}
/*
* Inject function
*/
void Inject_Game() {
INJECT(0x0044C550, StartGame);
INJECT(0x0044C6A0, GameLoop);
INJECT(0x0044C740, LevelCompleteSequence);
INJECT(0x0044C750, LevelStats);
INJECT(0x0044C920, GameStats);
INJECT(0x0044CA40, GetRandomControl);
INJECT(0x0044CA60, SeedRandomControl);
INJECT(0x0044CA70, GetRandomDraw);
INJECT(0x0044CA90, SeedRandomDraw);
INJECT(0x0044CAA0, GetValidLevelsList);
INJECT(0x0044CAF0, GetSavedGamesList);
INJECT(0x0044CB40, DisplayCredits);
INJECT(0x0044CD80, S_FrontEndCheck);
INJECT(0x0044CEF0, S_SaveGame);
INJECT(0x0044D010, S_LoadGame);
}
================================================
FILE: specific/game.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef GAME_H_INCLUDED
#define GAME_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
__int16 __cdecl StartGame(int levelID, GF_LEVEL_TYPE levelType); // 0x0044C550
int __cdecl GameLoop(BOOL demoMode); // 0x0044C6A0
int __cdecl LevelCompleteSequence(); // 0x0044C740
int __cdecl LevelStats(int levelID); // 0x0044C750
int __cdecl GameStats(int levelID); // 0x0044C920
int __cdecl GetRandomControl(); // 0x0044CA40
void __cdecl SeedRandomControl(int seed); // 0x0044CA60
int __cdecl GetRandomDraw(); // 0x0044CA70
void __cdecl SeedRandomDraw(int seed); // 0x0044CA90
void __cdecl GetValidLevelsList(REQUEST_INFO *req); // 0x0044CAA0
void __cdecl GetSavedGamesList(REQUEST_INFO *req); // 0x0044CAF0
void __cdecl DisplayCredits(); // 0x0044CB40
BOOL __cdecl S_FrontEndCheck(); // 0x0044CD80
BOOL __cdecl S_SaveGame(LPCVOID saveData, DWORD saveSize, int slotNumber); // 0x0044CEF0
BOOL __cdecl S_LoadGame(LPVOID saveData, DWORD saveSize, int saveNumber); // 0x0044D010
#endif // GAME_H_INCLUDED
================================================
FILE: specific/hwr.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/hwr.h"
#include "specific/init_display.h"
#include "specific/texture.h"
#include "global/vars.h"
#ifdef FEATURE_HUD_IMPROVED
#include "modding/psx_bar.h"
#endif // FEATURE_HUD_IMPROVED
#ifdef FEATURE_VIDEOFX_IMPROVED
DWORD AlphaBlendMode = 2;
typedef struct {
D3DBLEND src;
D3DBLEND dst;
BYTE alpha;
} BLEND_PARAM;
static BLEND_PARAM Blend[4] = {
{D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, 128},
{D3DBLEND_ONE, D3DBLEND_ONE, 255},
{D3DBLEND_ZERO, D3DBLEND_INVSRCCOLOR, 255},
{D3DBLEND_INVSRCALPHA, D3DBLEND_ONE, 192},
};
static void SetBlendMode(D3DTLVERTEX *vtxPtr, DWORD vtxCount, DWORD mode) {
if( !vtxPtr || !vtxCount || mode >= 4 ) return;
for( DWORD i = 0; i < vtxCount; ++i ) {
vtxPtr[i].color = RGBA_SETALPHA(vtxPtr[i].color, Blend[mode].alpha);
}
#if (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRS_SRCBLEND, Blend[mode].src);
D3DDev->SetRenderState(D3DRS_DESTBLEND, Blend[mode].dst);
#else // (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRENDERSTATE_SRCBLEND, Blend[mode].src);
D3DDev->SetRenderState(D3DRENDERSTATE_DESTBLEND, Blend[mode].dst);
#endif // (DIRECT3D_VERSION >= 0x900)
}
static void DrawAlphaBlended(D3DTLVERTEX *vtxPtr, DWORD vtxCount, DWORD mode) {
// do basic blending
SetBlendMode(vtxPtr, vtxCount, mode);
HWR_DrawPrimitive(D3DPT_TRIANGLEFAN, vtxPtr, vtxCount, true);
// do advanced blending
if( AlphaBlendMode == 2 && mode == 0 ) {
SetBlendMode(vtxPtr, vtxCount, 3); // additional quarter-additive blending pass
HWR_DrawPrimitive(D3DPT_TRIANGLEFAN, vtxPtr, vtxCount, true);
}
// return render states to default values
#if (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
D3DDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
#else // (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA);
D3DDev->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA);
#endif // (DIRECT3D_VERSION >= 0x900)
}
#endif // FEATURE_VIDEOFX_IMPROVED
// NOTE: this function is absent in the original code
HRESULT HWR_DrawPrimitive(D3DPRIMITIVETYPE primitiveType, LPVOID vertices, DWORD vertexCount, bool isNoClip) {
#if (DIRECT3D_VERSION >= 0x900)
extern LPDIRECT3DVERTEXBUFFER9 D3DVtx;
int primitiveCount = 0;
switch( primitiveType ) {
case D3DPT_POINTLIST: primitiveCount = vertexCount; break;
case D3DPT_LINELIST: primitiveCount = vertexCount/2; break;
case D3DPT_LINESTRIP: primitiveCount = vertexCount-1; break;
case D3DPT_TRIANGLELIST: primitiveCount = vertexCount/3; break;
case D3DPT_TRIANGLESTRIP: primitiveCount = vertexCount-2; break;
case D3DPT_TRIANGLEFAN: primitiveCount = vertexCount-2; break;
default: break;
}
if( primitiveCount <= 0 ) {
return D3DERR_INVALIDCALL;
}
static DWORD vertexIndex = 0;
DWORD flags = D3DLOCK_NOOVERWRITE;
if( vertexIndex + vertexCount > VTXBUF_LEN ) {
vertexIndex = 0;
flags = D3DLOCK_DISCARD;
}
LPVOID ptr = NULL;
HRESULT res = D3DVtx->Lock(sizeof(D3DTLVERTEX) * vertexIndex, sizeof(D3DTLVERTEX) * vertexCount, &ptr, flags);
if FAILED(res) return res;
memcpy(ptr, vertices, sizeof(D3DTLVERTEX) * vertexCount);
D3DVtx->Unlock();
res = D3DDev->DrawPrimitive(primitiveType, vertexIndex, primitiveCount);
vertexIndex += vertexCount;
return res;
#else // (DIRECT3D_VERSION >= 0x900)
return D3DDev->DrawPrimitive(primitiveType, D3DVT_TLVERTEX, vertices, vertexCount, isNoClip ? D3DDP_DONOTUPDATEEXTENTS|D3DDP_DONOTCLIP : 0);
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __cdecl HWR_InitState() {
#if (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRS_CLIPPING, FALSE);
D3DDev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
D3DDev->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
D3DDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
D3DDev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
D3DDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
D3DDev->SetRenderState(D3DRS_DITHERENABLE, SavedAppSettings.Dither ? TRUE : FALSE);
AlphaBlendEnabler = D3DRS_ALPHABLENDENABLE;
#if defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
D3DDev->SetTextureStageState(0, D3DTSS_COLOROP, SavedAppSettings.LightingMode ? D3DTOP_MODULATE2X : D3DTOP_MODULATE);
#else // defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
D3DDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
#endif // defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
D3DDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
D3DDev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
D3DDev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
D3DDev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
D3DDev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
DWORD filter = SavedAppSettings.BilinearFiltering ? D3DTEXF_LINEAR : D3DTEXF_POINT;
D3DDev->SetSamplerState(0, D3DSAMP_MAGFILTER, filter);
D3DDev->SetSamplerState(0, D3DSAMP_MINFILTER, filter);
D3DDev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
D3DDev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
#else // (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRENDERSTATE_FILLMODE, D3DFILL_SOLID);
D3DDev->SetRenderState(D3DRENDERSTATE_SHADEMODE, D3DSHADE_GOURAUD);
D3DDev->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
D3DDev->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA);
D3DDev->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA);
D3DDev->SetRenderState(D3DRENDERSTATE_TEXTUREPERSPECTIVE, SavedAppSettings.PerspectiveCorrect ? TRUE : FALSE);
D3DDev->SetRenderState(D3DRENDERSTATE_DITHERENABLE, SavedAppSettings.Dither ? TRUE : FALSE);
AlphaBlendEnabler = CurrentDisplayAdapter.shadeRestricted ? D3DRENDERSTATE_STIPPLEDALPHA : D3DRENDERSTATE_ALPHABLENDENABLE;
DWORD filter = SavedAppSettings.BilinearFiltering ? D3DFILTER_LINEAR : D3DFILTER_NEAREST;
DWORD blend = (CurrentDisplayAdapter.D3DHWDeviceDesc.dpcTriCaps.dwTextureBlendCaps & D3DPTBLENDCAPS_MODULATEALPHA) ? D3DTBLEND_MODULATEALPHA : D3DTBLEND_MODULATE;
D3DDev->SetRenderState(D3DRENDERSTATE_TEXTUREMAG, filter);
D3DDev->SetRenderState(D3DRENDERSTATE_TEXTUREMIN, filter);
D3DDev->SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND, blend);
// NOTE: the next line is absent in the original game, but it fixes a texture bleeding in some cases
D3DDev->SetRenderState(D3DRENDERSTATE_TEXTUREADDRESS, D3DTADDRESS_CLAMP);
#endif // (DIRECT3D_VERSION >= 0x900)
HWR_ResetTexSource();
HWR_ResetColorKey();
HWR_ResetZBuffer();
}
void __cdecl HWR_ResetTexSource() {
CurrentTexSource = 0;
#if (DIRECT3D_VERSION >= 0x900)
D3DDev->SetTexture(0, NULL);
#else // (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRENDERSTATE_TEXTUREHANDLE, 0);
D3DDev->SetRenderState(D3DRENDERSTATE_FLUSHBATCH, 0);
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __cdecl HWR_ResetColorKey() {
ColorKeyState = FALSE;
#if (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
#else // (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(TexturesAlphaChannel ? D3DRENDERSTATE_ALPHABLENDENABLE : D3DRENDERSTATE_COLORKEYENABLE, FALSE);
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __cdecl HWR_ResetZBuffer() {
ZEnableState = FALSE;
ZWriteEnableState = FALSE;
#if (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
D3DDev->SetRenderState(D3DRS_ZWRITEENABLE, D3DZB_FALSE);
#else // (DIRECT3D_VERSION >= 0x900)
if( ZBufferSurface != NULL ) {
D3DDev->SetRenderState(D3DRENDERSTATE_ZFUNC, D3DCMP_ALWAYS);
D3DDev->SetRenderState(D3DRENDERSTATE_ZENABLE, SavedAppSettings.ZBuffer ? TRUE : FALSE);
} else {
D3DDev->SetRenderState(D3DRENDERSTATE_ZFUNC, D3DCMP_LESSEQUAL);
D3DDev->SetRenderState(D3DRENDERSTATE_ZENABLE, FALSE);
}
D3DDev->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, FALSE);
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __cdecl HWR_TexSource(HWR_TEXHANDLE texSource) {
if( CurrentTexSource != texSource ) {
#if (DIRECT3D_VERSION >= 0x900)
D3DDev->SetTexture(0, texSource);
#else // (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRENDERSTATE_TEXTUREHANDLE, texSource);
#endif // (DIRECT3D_VERSION >= 0x900)
CurrentTexSource = texSource;
}
}
void __cdecl HWR_EnableColorKey(bool state) {
if( ColorKeyState != state ) {
#if (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRS_ALPHABLENDENABLE, state ? TRUE : FALSE);
#else // (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(TexturesAlphaChannel ? D3DRENDERSTATE_ALPHABLENDENABLE : D3DRENDERSTATE_COLORKEYENABLE, state ? TRUE : FALSE);
#endif // (DIRECT3D_VERSION >= 0x900)
ColorKeyState = state;
}
}
void __cdecl HWR_EnableZBuffer(bool ZWriteEnable, bool ZEnable) {
if( !SavedAppSettings.ZBuffer )
return;
if( ZWriteEnableState != ZWriteEnable ) {
#if (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRS_ZWRITEENABLE, ZWriteEnable ? D3DZB_TRUE : D3DZB_FALSE);
#else // (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRENDERSTATE_ZWRITEENABLE, ZWriteEnable ? TRUE : FALSE);
#endif // (DIRECT3D_VERSION >= 0x900)
ZWriteEnableState = ZWriteEnable;
}
if( ZEnableState != ZEnable ) {
#if (DIRECT3D_VERSION >= 0x900)
D3DDev->SetRenderState(D3DRS_ZENABLE, ZEnable ? D3DZB_TRUE : D3DZB_FALSE);
#else // (DIRECT3D_VERSION >= 0x900)
if( ZBufferSurface != NULL )
D3DDev->SetRenderState(D3DRENDERSTATE_ZFUNC, ZEnable ? D3DCMP_LESSEQUAL : D3DCMP_ALWAYS);
else
D3DDev->SetRenderState(D3DRENDERSTATE_ZENABLE, ZEnable ? TRUE : FALSE);
#endif // (DIRECT3D_VERSION >= 0x900)
ZEnableState = ZEnable;
}
}
void __cdecl HWR_BeginScene() {
HWR_GetPageHandles();
#if (DIRECT3D_VERSION < 0x900)
WaitPrimaryBufferFlip();
#endif // (DIRECT3D_VERSION < 0x900)
D3DDev->BeginScene();
}
void __cdecl HWR_DrawPolyList() {
DWORD alphaState;
UINT16 *bufPtr;
UINT16 polyType, texPage, vtxCount;
D3DTLVERTEX *vtxPtr;
HWR_EnableZBuffer(false, true);
for( DWORD i=0; iGetRenderState(AlphaBlendEnabler, &alphaState);
D3DDev->SetRenderState(AlphaBlendEnabler, TRUE);
HWR_DrawPrimitive(D3DPT_TRIANGLEFAN, vtxPtr, vtxCount, true);
D3DDev->SetRenderState(AlphaBlendEnabler, alphaState);
break;
}
}
}
void __cdecl HWR_LoadTexturePages(int pagesCount, LPVOID pagesBuffer, RGB888 *palette) {
int pageIndex = -1;
BYTE *bufferPtr = (BYTE *)pagesBuffer;
HWR_FreeTexturePages();
if( palette != NULL )
PaletteIndex = CreateTexturePalette(palette);
#if (DIRECT3D_VERSION >= 0x900)
char levelName[256] = {0};
char texName[256] = {0};
strncpy(levelName, PathFindFileName(LevelFileName), sizeof(levelName)-1);
char *ext = PathFindExtension(levelName);
if( ext != NULL ) *ext = 0;
#endif // (DIRECT3D_VERSION >= 0x900)
for( int i=0; i= 0x900)
snprintf(texName, sizeof(texName), "./textures/texpages/%s_%d.png", levelName, i);
#endif // (DIRECT3D_VERSION >= 0x900)
if( palette != NULL ) {
pageIndex = AddTexturePage8(256, 256, bufferPtr, PaletteIndex);
bufferPtr += 256*256*1;
#if (DIRECT3D_VERSION >= 0x900)
} else if( PathFileExists(texName) ) {
pageIndex = AddExternalTexture(texName, true);
bufferPtr += 256*256*2;
#endif // (DIRECT3D_VERSION >= 0x900)
} else {
pageIndex = AddTexturePage16(256, 256, bufferPtr);
bufferPtr += 256*256*2;
}
HWR_TexturePageIndexes[i] = (pageIndex < 0) ? -1 : pageIndex;
}
HWR_GetPageHandles();
}
void __cdecl HWR_FreeTexturePages() {
for( DWORD i=0; i= 0 ) {
SafeFreeTexturePage(HWR_TexturePageIndexes[i]);
HWR_TexturePageIndexes[i] = -1;
}
HWR_PageHandles[i] = 0;
}
if( PaletteIndex >= 0 ) {
SafeFreePalette(PaletteIndex);
}
}
void __cdecl HWR_GetPageHandles() {
for( DWORD i=0; i= ARRAY_SIZE(HWR_VertexBuffer) - 0x200);
}
bool __cdecl HWR_Init() {
memset(HWR_VertexBuffer, 0, sizeof(HWR_VertexBuffer));
memset(HWR_TexturePageIndexes, 0xFF, sizeof(HWR_TexturePageIndexes)); // fill indexes by -1
return true;
}
/*
* Inject function
*/
void Inject_HWR() {
INJECT(0x0044D0B0, HWR_InitState);
INJECT(0x0044D1E0, HWR_ResetTexSource);
INJECT(0x0044D210, HWR_ResetColorKey);
INJECT(0x0044D240, HWR_ResetZBuffer);
INJECT(0x0044D2A0, HWR_TexSource);
INJECT(0x0044D2D0, HWR_EnableColorKey);
INJECT(0x0044D320, HWR_EnableZBuffer);
INJECT(0x0044D3B0, HWR_BeginScene);
INJECT(0x0044D3E0, HWR_DrawPolyList);
INJECT(0x0044D560, HWR_LoadTexturePages);
INJECT(0x0044D5F0, HWR_FreeTexturePages);
INJECT(0x0044D640, HWR_GetPageHandles);
INJECT(0x0044D680, HWR_VertexBufferFull);
INJECT(0x0044D6B0, HWR_Init);
}
================================================
FILE: specific/hwr.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef HWR_H_INCLUDED
#define HWR_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// NOTE: this function is absent in the original code
HRESULT HWR_DrawPrimitive(D3DPRIMITIVETYPE primitiveType, LPVOID vertices, DWORD vertexCount, bool isNoClip);
void __cdecl HWR_InitState(); // 0x0044D0B0
void __cdecl HWR_ResetTexSource(); // 0x0044D1E0
void __cdecl HWR_ResetColorKey(); // 0x0044D210
void __cdecl HWR_ResetZBuffer(); // 0x0044D240
void __cdecl HWR_TexSource(HWR_TEXHANDLE texSource); // 0x0044D2A0
void __cdecl HWR_EnableColorKey(bool state); // 0x0044D2D0
void __cdecl HWR_EnableZBuffer(bool ZWriteEnable, bool ZEnable); // 0x0044D320
void __cdecl HWR_BeginScene(); // 0x0044D3B0
void __cdecl HWR_DrawPolyList(); // 0x0044D3E0
void __cdecl HWR_LoadTexturePages(int pagesCount, LPVOID pagesBuffer, RGB888 *palette); // 0x0044D560
void __cdecl HWR_FreeTexturePages(); // 0x0044D5F0
void __cdecl HWR_GetPageHandles(); // 0x0044D640
bool __cdecl HWR_VertexBufferFull(); // 0x0044D680
bool __cdecl HWR_Init(); // 0x0044D6B0
#endif // HWR_INCLUDED
================================================
FILE: specific/init.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/init.h"
#include "3dsystem/phd_math.h"
#include "specific/game.h"
#include "specific/winmain.h"
#include "global/vars.h"
#include
// related to GAMEALLOC_BUFFER enum
static LPCTSTR BufferNames[] = {
"Temp Alloc",
"Texture Pages",
"Mesh Pointers",
"Meshes",
"Anims",
"Structs",
"Ranges",
"Commands",
"Bones",
"Frames",
"Room Textures",
"Room Infos",
"Room Mesh",
"Room Door",
"Room Floor",
"Room Lights",
"Room Static Mesh Infos",
"Floor Data",
"ITEMS!!",
"Cameras",
"Sound FX",
"Boxes",
"Overlaps",
"GroundZone",
"FlyZone",
"Animating Texture Ranges",
"Cinematic Frames",
"LoadDemo Buffer",
"SaveDemo Buffer",
"Cinematic Effects",
"Mummy Head Turn",
"Extra Door stuff",
"Effects_Array",
"Creature Data",
"Creature LOT",
"Sample Infos",
"Samples",
"Sample Offsets",
"Rolling Ball Stuff",
"Skidoo Stuff",
"Load Piccy Buffer",
"FMV Buffers",
"Polygon Buffers",
"Order Tables",
"CLUTs",
"Texture Infos",
"Sprite Infos",
};
BOOL __cdecl S_InitialiseSystem() {
S_SeedRandom();
DumpX = 0;
DumpY = 0;
DumpWidth = GameVidWidth;
DumpHeight = GameVidHeight;
CalculateWibbleTable();
#ifdef FEATURE_EXTENDED_LIMITS
GameMemorySize = 0x1000000; // 16 MB
#else // FEATURE_EXTENDED_LIMITS
GameMemorySize = 0x380000; // 3.5 MB
#endif // FEATURE_EXTENDED_LIMITS
return TRUE;
}
void __cdecl ShutdownGame() {
if( GameMemoryPointer != NULL ) {
GlobalFree(GameMemoryPointer);
GameMemoryPointer = NULL;
}
}
void __cdecl init_game_malloc() {
GameAllocMemPointer = GameMemoryPointer;
GameAllocMemFree = GameMemorySize;
GameAllocMemUsed = 0;
}
void *__cdecl game_malloc(DWORD allocSize, DWORD bufIndex) {
DWORD alignedSize = (allocSize + 3) & ~3;
if( alignedSize > GameAllocMemFree ) {
wsprintf(StringToShow, "game_malloc(): OUT OF MEMORY %s %d", BufferNames[bufIndex], alignedSize);
S_ExitSystem(StringToShow);
return NULL; // the app is terminated here
}
void *result = GameAllocMemPointer;
GameAllocMemFree -= alignedSize;
GameAllocMemUsed += alignedSize;
GameAllocMemPointer += alignedSize;
return result;
}
void __cdecl game_free(DWORD freeSize) {
DWORD alignedSize = (freeSize + 3) & ~3;
GameAllocMemPointer -= alignedSize;
GameAllocMemFree += alignedSize;
GameAllocMemUsed -= alignedSize;
}
void __cdecl CalculateWibbleTable() {
// This function calculates water effect tables
for( int i=0; i < WIBBLE_SIZE; ++i ) {
int sine = phd_sin(i * PHD_360 / WIBBLE_SIZE); // 360 degrees divided by wibble number
WibbleTable[i] = sine * MAX_WIBBLE >> W2V_SHIFT;
ShadesTable[i] = sine * MAX_SHADE >> W2V_SHIFT;
RandomTable[i] = (GetRandomDraw() >> 5) - 0x01FF;
for( int j=0; j < WIBBLE_SIZE; ++j )
RoomLightTables[i].table[j] = (j - (WIBBLE_SIZE/2)) * i * MAX_ROOMLIGHT_UNIT / (WIBBLE_SIZE-1);
}
}
void __cdecl S_SeedRandom() {
time_t t = time(NULL);
struct tm *ltm = localtime(&t);
SeedRandomControl(ltm->tm_sec + 57 * ltm->tm_min + 3543 * ltm->tm_hour);
SeedRandomDraw(ltm->tm_sec + 43 * ltm->tm_min + 3477 * ltm->tm_hour);
}
/*
* Inject function
*/
void Inject_Init() {
INJECT(0x0044D6E0, S_InitialiseSystem);
INJECT(0x0044D730, ShutdownGame);
INJECT(0x0044D750, init_game_malloc);
INJECT(0x0044D780, game_malloc);
INJECT(0x0044D800, game_free);
INJECT(0x0044D840, CalculateWibbleTable);
INJECT(0x0044D930, S_SeedRandom);
}
================================================
FILE: specific/init.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef INIT_H_INCLUDED
#define INIT_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
BOOL __cdecl S_InitialiseSystem(); // 0x0044D6E0
void __cdecl ShutdownGame(); // 0x0044D730
void __cdecl init_game_malloc(); // 0x0044D750
void *__cdecl game_malloc(DWORD allocSize, DWORD bufIndex); // 0x0044D780
void __cdecl game_free(DWORD freeSize); // 0x0044D800
void __cdecl CalculateWibbleTable(); // 0x0044D840
void __cdecl S_SeedRandom(); // 0x0044D930
#endif // INIT_H_INCLUDED
================================================
FILE: specific/init_3d.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/init_3d.h"
#include "global/vars.h"
#if (DIRECT3D_VERSION >= 0x900)
LPDIRECT3DVERTEXBUFFER9 D3DVtx = NULL;
#endif // (DIRECT3D_VERSION >= 0x900)
#if (DIRECT3D_VERSION < 0x900)
void __cdecl Enumerate3DDevices(DISPLAY_ADAPTER *adapter) {
if( D3DCreate() ) {
D3D->EnumDevices(Enum3DDevicesCallback, (LPVOID)adapter);
D3DRelease();
}
}
#endif // (DIRECT3D_VERSION < 0x900)
bool __cdecl D3DCreate() {
#if (DIRECT3D_VERSION >= 0x900)
D3D = Direct3DCreate9(D3D_SDK_VERSION);
return ( D3D != NULL );
#else // (DIRECT3D_VERSION >= 0x900)
return SUCCEEDED(DDraw->QueryInterface(IID_IDirect3D2, (LPVOID *)&D3D));
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __cdecl D3DRelease() {
if( D3D ) {
D3D->Release();
D3D = NULL;
}
}
#if (DIRECT3D_VERSION < 0x900)
HRESULT CALLBACK Enum3DDevicesCallback(GUID FAR* lpGuid, LPTSTR lpDeviceDescription, LPTSTR lpDeviceName, LPD3DDEVICEDESC lpD3DHWDeviceDesc, LPD3DDEVICEDESC lpD3DHELDeviceDesc, LPVOID lpContext) {
DISPLAY_ADAPTER *adapter = (DISPLAY_ADAPTER *)lpContext;
if( lpD3DHWDeviceDesc && D3DIsSupported(lpD3DHWDeviceDesc) ) {
adapter->hwRenderSupported = true;
adapter->deviceGuid = *lpGuid;
adapter->D3DHWDeviceDesc = *lpD3DHWDeviceDesc;
adapter->perspectiveCorrectSupported = (lpD3DHWDeviceDesc->dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_PERSPECTIVE)? true:false;
adapter->ditherSupported = (lpD3DHWDeviceDesc->dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_DITHER)? true:false;
adapter->zBufferSupported = (lpD3DHWDeviceDesc->dwDeviceZBufferBitDepth)? true:false;
adapter->linearFilterSupported = (lpD3DHWDeviceDesc->dpcTriCaps.dwTextureFilterCaps & D3DPTFILTERCAPS_LINEAR)? true:false;
adapter->shadeRestricted = (lpD3DHWDeviceDesc->dpcTriCaps.dwShadeCaps & (D3DPSHADECAPS_ALPHAGOURAUDBLEND|D3DPSHADECAPS_ALPHAFLATBLEND))? false:true;
}
return D3DENUMRET_OK;
}
bool __cdecl D3DIsSupported(LPD3DDEVICEDESC desc) {
if( (desc->dwFlags & D3DDD_COLORMODEL) &&
(desc->dcmColorModel & D3DCOLOR_RGB) &&
(desc->dwFlags & D3DDD_TRICAPS) &&
(desc->dpcTriCaps.dwShadeCaps & D3DPSHADECAPS_COLORGOURAUDRGB) &&
(desc->dpcTriCaps.dwTextureBlendCaps & D3DPTBLENDCAPS_MODULATE) )
{
return true;
}
return false;
}
bool __cdecl D3DSetViewport() {
D3DVIEWPORT2 viewPort;
viewPort.dwSize = sizeof(D3DVIEWPORT2);
viewPort.dvClipX = 0.0;
viewPort.dvClipY = 0.0;
viewPort.dvClipWidth = (float)GameVidWidth;
viewPort.dvClipHeight = (float)GameVidHeight;
viewPort.dwX = 0;
viewPort.dwY = 0;
viewPort.dwWidth = GameVidWidth;
viewPort.dwHeight = GameVidHeight;
viewPort.dvMinZ = 0.0;
viewPort.dvMaxZ = 1.0;
if FAILED(D3DView->SetViewport2(&viewPort)) {
D3DView->GetViewport2(&viewPort);
return false;
}
return SUCCEEDED(D3DDev->SetCurrentViewport(D3DView));
}
#endif // (DIRECT3D_VERSION < 0x900)
void __cdecl D3DDeviceCreate(LPDDS lpBackBuffer) {
if( D3D == NULL && !D3DCreate() )
throw ERR_D3D_Create;
#if (DIRECT3D_VERSION >= 0x900)
D3DPRESENT_PARAMETERS d3dpp;
memset(&d3dpp, 0, sizeof(d3dpp));
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = HGameWindow;
d3dpp.EnableAutoDepthStencil = TRUE;
D3DCAPS9 *caps = &SavedAppSettings.PreferredDisplayAdapter->body.caps;
if SUCCEEDED(D3D->CheckDepthStencilMatch(caps->AdapterOrdinal, caps->DeviceType, D3DFMT_X8R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_D16)) {
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
} else if SUCCEEDED(D3D->CheckDepthStencilMatch(caps->AdapterOrdinal, caps->DeviceType, D3DFMT_X8R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_D24X8)) {
d3dpp.AutoDepthStencilFormat = D3DFMT_D24X8;
} else {
throw ERR_CreateDevice;
}
d3dpp.Windowed = !SavedAppSettings.FullScreen;
if( SavedAppSettings.FullScreen ) {
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferWidth = FullScreenWidth;
d3dpp.BackBufferHeight = FullScreenHeight;
}
if( D3DDev ) {
if( D3DVtx != NULL ) {
D3DVtx->Release();
D3DVtx = NULL;
}
HRESULT res = D3D_OK;
do {
res = D3DDev->TestCooperativeLevel();
} while( res == D3DERR_DEVICELOST );
if( (res != D3D_OK && res != D3DERR_DEVICENOTRESET) || FAILED(D3DDev->Reset(&d3dpp)) ) {
throw ERR_CreateDevice;
}
} else {
DWORD flags = CHK_ANY(caps->DevCaps, D3DDEVCAPS_HWTRANSFORMANDLIGHT) ? D3DCREATE_HARDWARE_VERTEXPROCESSING : D3DCREATE_SOFTWARE_VERTEXPROCESSING;
if FAILED(D3D->CreateDevice(CurrentDisplayAdapter.index, D3DDEVTYPE_HAL, HGameWindow, flags, &d3dpp, &D3DDev)) {
throw ERR_CreateDevice;
}
}
if( !D3DVtx && FAILED(D3DDev->CreateVertexBuffer(sizeof(D3DTLVERTEX) * VTXBUF_LEN, D3DUSAGE_WRITEONLY|D3DUSAGE_DONOTCLIP|D3DUSAGE_DYNAMIC, D3DFVF_TLVERTEX, D3DPOOL_DEFAULT, &D3DVtx, NULL)) )
throw ERR_CreateDevice;
D3DDev->SetStreamSource(0, D3DVtx, 0, sizeof(D3DTLVERTEX));
D3DDev->SetFVF(D3DFVF_TLVERTEX);
#else // (DIRECT3D_VERSION >= 0x900)
if FAILED(D3D->CreateDevice(IID_IDirect3DHALDevice, (LPDIRECTDRAWSURFACE)lpBackBuffer, &D3DDev))
throw ERR_CreateDevice;
if FAILED(D3D->CreateViewport(&D3DView, NULL))
throw ERR_CreateViewport;
if FAILED(D3DDev->AddViewport(D3DView))
throw ERR_AddViewport;
if FAILED(!D3DSetViewport())
throw ERR_SetViewport2;
if FAILED(D3D->CreateMaterial(&D3DMaterial, NULL))
throw ERR_CreateViewport;
D3DMATERIAL matData;
D3DMATERIALHANDLE matHandle;
memset(&matData, 0, sizeof(matData));
matData.dwSize = sizeof(matData);
if FAILED(D3DMaterial->SetMaterial(&matData))
throw ERR_CreateViewport;
if FAILED(D3DMaterial->GetHandle(D3DDev, &matHandle))
throw ERR_CreateViewport;
if FAILED(D3DView->SetBackground(matHandle))
throw ERR_CreateViewport;
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __cdecl Direct3DRelease() {
#if (DIRECT3D_VERSION >= 0x900)
if( D3DVtx != NULL ) {
D3DVtx->Release();
D3DVtx = NULL;
}
#else // (DIRECT3D_VERSION >= 0x900)
if( D3DMaterial != NULL ) {
D3DMaterial->Release();
D3DMaterial = NULL;
}
if( D3DView != NULL ) {
D3DView->Release();
D3DView = NULL;
}
#endif // (DIRECT3D_VERSION >= 0x900)
if( D3DDev != NULL ) {
D3DDev->Release();
D3DDev = NULL;
}
D3DRelease();
}
bool __cdecl Direct3DInit() {
return true;
}
/*
* Inject function
*/
void Inject_Init3d() {
#if (DIRECT3D_VERSION < 0x900)
INJECT(0x004445F0, Enumerate3DDevices);
#endif // (DIRECT3D_VERSION < 0x900)
INJECT(0x00444620, D3DCreate);
INJECT(0x00444640, D3DRelease);
#if (DIRECT3D_VERSION < 0x900)
INJECT(0x00444660, Enum3DDevicesCallback);
INJECT(0x00444720, D3DIsSupported);
INJECT(0x00444760, D3DSetViewport);
#endif // (DIRECT3D_VERSION < 0x900)
INJECT(0x00444820, D3DDeviceCreate);
INJECT(0x004449E0, Direct3DRelease);
INJECT(0x00444A30, Direct3DInit);
}
================================================
FILE: specific/init_3d.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef INIT_3D_H_INCLUDED
#define INIT_3D_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#if (DIRECT3D_VERSION < 0x900)
void __cdecl Enumerate3DDevices(DISPLAY_ADAPTER *adapter); // 0x004445F0
#endif // (DIRECT3D_VERSION < 0x900)
bool __cdecl D3DCreate(); // 0x00444620
void __cdecl D3DRelease(); // 0x00444640
#if (DIRECT3D_VERSION < 0x900)
HRESULT CALLBACK Enum3DDevicesCallback(GUID FAR* lpGuid, LPTSTR lpDeviceDescription, LPTSTR lpDeviceName, LPD3DDEVICEDESC lpD3DHWDeviceDesc, LPD3DDEVICEDESC lpD3DHELDeviceDesc, LPVOID lpContext); // 0x00444660
bool __cdecl D3DIsSupported(LPD3DDEVICEDESC desc); // 0x00444720
bool __cdecl D3DSetViewport(); // 0x00444760
#endif // (DIRECT3D_VERSION < 0x900)
void __cdecl D3DDeviceCreate(LPDDS lpBackBuffer); // 0x00444820
void __cdecl Direct3DRelease(); // 0x004449E0
bool __cdecl Direct3DInit(); // 0x00444A30
#endif // INIT_3D_H_INCLUDED
================================================
FILE: specific/init_display.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "game/health.h"
#include "specific/display.h"
#include "specific/init_display.h"
#include "specific/file.h"
#include "specific/hwr.h"
#include "specific/init_3d.h"
#include "specific/output.h"
#include "specific/texture.h"
#include "specific/winmain.h"
#include "specific/winvid.h"
#include "global/vars.h"
#ifdef FEATURE_BACKGROUND_IMPROVED
#include "modding/background_new.h"
extern DWORD BGND_PictureWidth;
extern DWORD BGND_PictureHeight;
#endif // FEATURE_BACKGROUND_IMPROVED
// Related to ERROR_CODE enum
static LPCTSTR ErrorStringTable[] = {
"OK",
"PreferredAdapterNotFound",
"CantCreateWindow",
"CantCreateDirectDraw",
"CantInitRenderer",
"CantCreateDirectInput",
"CantCreateKeyboardDevice",
"CantSetKBCooperativeLevel",
"CantSetKBDataFormat",
"CantAcquireKeyboard",
"CantSetDSCooperativeLevel",
"DD_SetExclusiveMode",
"DD_ClearExclusiveMode",
"SetDisplayMode",
"CreateScreenBuffers",
"GetBackBuffer",
"CreatePalette",
"SetPalette",
"CreatePrimarySurface",
"CreateBackBuffer",
"CreateClipper",
"SetClipperHWnd",
"SetClipper",
"CreateZBuffer",
"AttachZBuffer",
"CreateRenderBuffer",
"CreatePictureBuffer",
"D3D_Create",
"CreateDevice",
"CreateViewport",
"AddViewport",
"SetViewport2",
"SetCurrentViewport",
"ClearRenderBuffer",
"UpdateRenderInfo",
"GetThirdBuffer",
"GoFullScreen",
"GoWindowed",
"WrongBitDepth",
"GetPixelFormat",
"GetDisplayMode",
};
#if (DIRECT3D_VERSION >= 0x900)
#ifdef FEATURE_VIDEOFX_IMPROVED
extern DWORD ReflectionMode;
#endif // FEATURE_VIDEOFX_IMPROVED
SWR_BUFFER RenderBuffer = {0, 0, NULL};
SWR_BUFFER PictureBuffer = {0, 0, NULL};
static bool SWRBufferCreate(SWR_BUFFER *buffer, DWORD width, DWORD height) {
if( !buffer || !width || !height ) return false;
buffer->width = width;
buffer->height = height;
buffer->bitmap = (LPBYTE)calloc(1, width*height);
return ( buffer->bitmap != NULL );
}
static void SWRBufferFree(SWR_BUFFER *buffer) {
if( !buffer || !buffer->bitmap ) return;
free(buffer->bitmap);
buffer->bitmap = NULL;
}
static bool SWRBufferClear(SWR_BUFFER *buffer, BYTE value) {
if( !buffer || !buffer->bitmap || !buffer->width || !buffer->height ) return false;
memset(buffer->bitmap, value, buffer->width * buffer->height);
return true;
}
LPDDS CaptureBufferSurface = NULL;
static void FreeCaptureBuffer() {
if( CaptureBufferSurface != NULL) {
CaptureBufferSurface->Release();
CaptureBufferSurface = NULL;
}
}
static int CreateCaptureBuffer() {
FreeCaptureBuffer();
if FAILED(D3DDev->CreateRenderTarget(GameVidWidth, GameVidHeight, D3DFMT_X8R8G8B8, D3DMULTISAMPLE_NONE, 0, TRUE, &CaptureBufferSurface, NULL))
return -1;
D3DDev->ColorFill(CaptureBufferSurface, NULL, 0);
DDSDESC desc;
if FAILED(CaptureBufferSurface->LockRect(&desc, NULL, 0)) {
FreeCaptureBuffer();
return -1;
}
CaptureBufferSurface->UnlockRect();
return 0;
}
#else // (DIRECT3D_VERSION >= 0x900)
#if defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED) || defined(FEATURE_VIDEOFX_IMPROVED)
LPDDS CaptureBufferSurface = NULL; // used for screen capture in windowed mode
LPDDS EnvmapBufferSurface = NULL; // used as environment map for the reflection effect
static int CreateCaptureBuffer() {
DDSDESC dsp;
memset(&dsp, 0, sizeof(dsp));
dsp.dwSize = sizeof(dsp);
dsp.dwFlags = DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS;
dsp.dwWidth = GameVidBufWidth;
dsp.dwHeight = GameVidBufHeight;
dsp.ddsCaps.dwCaps = DDSCAPS_3DDEVICE|DDSCAPS_OFFSCREENPLAIN;
if( SavedAppSettings.RenderMode == RM_Hardware )
dsp.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
if FAILED(DDrawSurfaceCreate(&dsp, &CaptureBufferSurface))
return -1;
WinVidClearBuffer(CaptureBufferSurface, NULL, 0);
return 0;
}
#endif // defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED) || defined(FEATURE_VIDEOFX_IMPROVED)
void __cdecl CreateScreenBuffers() {
DDSCAPS ddsCaps;
DDSDESC dsp;
memset(&dsp, 0, sizeof(dsp));
dsp.dwSize = sizeof(dsp);
dsp.dwFlags = DDSD_BACKBUFFERCOUNT|DDSD_CAPS;
dsp.dwBackBufferCount = (SavedAppSettings.TripleBuffering) ? 2 : 1;
dsp.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX;
if( SavedAppSettings.RenderMode == RM_Hardware )
dsp.ddsCaps.dwCaps |= DDSCAPS_3DDEVICE;
if FAILED(DDrawSurfaceCreate(&dsp, &PrimaryBufferSurface))
throw ERR_CreateScreenBuffers;
WinVidClearBuffer(PrimaryBufferSurface, NULL, 0);
memset(&ddsCaps, 0, sizeof(ddsCaps));
ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
if FAILED(PrimaryBufferSurface->GetAttachedSurface(&ddsCaps, &BackBufferSurface))
throw ERR_GetBackBuffer;
WinVidClearBuffer(BackBufferSurface, NULL, 0);
if( SavedAppSettings.TripleBuffering ) {
memset(&ddsCaps, 0, sizeof(ddsCaps));
ddsCaps.dwCaps = DDSCAPS_FLIP;
if FAILED(BackBufferSurface->GetAttachedSurface(&ddsCaps, &ThirdBufferSurface))
throw ERR_GetThirdBuffer;
WinVidClearBuffer(ThirdBufferSurface, NULL, 0);
}
}
void __cdecl CreatePrimarySurface() {
DDSDESC dsp;
if( ( GameVid_IsVga && SavedAppSettings.RenderMode == RM_Hardware) ||
(!GameVid_IsVga && SavedAppSettings.RenderMode == RM_Software) )
throw ERR_WrongBitDepth;
memset(&dsp, 0, sizeof(dsp));
dsp.dwSize = sizeof(dsp);
dsp.dwFlags = DDSD_CAPS;
dsp.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
if FAILED(DDrawSurfaceCreate(&dsp, &PrimaryBufferSurface))
throw ERR_CreatePrimarySurface;
}
void __cdecl CreateBackBuffer() {
DDSDESC dsp;
memset(&dsp, 0, sizeof(dsp));
dsp.dwSize = sizeof(dsp);
dsp.dwFlags = DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS;
dsp.dwWidth = GameVidBufWidth;
dsp.dwHeight = GameVidBufHeight;
dsp.ddsCaps.dwCaps = DDSCAPS_3DDEVICE|DDSCAPS_OFFSCREENPLAIN;
if( SavedAppSettings.RenderMode == RM_Hardware )
dsp.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
if FAILED(DDrawSurfaceCreate(&dsp, &BackBufferSurface))
throw ERR_CreateBackBuffer;
WinVidClearBuffer(BackBufferSurface, NULL, 0);
}
void __cdecl CreateClipper() {
if FAILED(DDraw->CreateClipper(0, &DDrawClipper, NULL))
throw ERR_CreateClipper;
if FAILED(DDrawClipper->SetHWnd(0, HGameWindow))
throw ERR_SetClipperHWnd;
if FAILED(PrimaryBufferSurface->SetClipper(DDrawClipper))
throw ERR_SetClipper;
}
void __cdecl CreateWindowPalette() {
int i;
DWORD dwFlags = DDPCAPS_8BIT;
memset(WinVidPalette, 0, sizeof(WinVidPalette));
if( GameVid_IsWindowedVga ) {
for( i=0; i<10; ++i ) {
WinVidPalette[i].peFlags = PC_EXPLICIT;
WinVidPalette[i].peRed = i;
}
for( i=10; i<246; ++i ) {
WinVidPalette[i].peFlags = PC_NOCOLLAPSE | PC_RESERVED;
}
for( i=246; i<256; ++i ) {
WinVidPalette[i].peFlags = PC_EXPLICIT;
WinVidPalette[i].peRed = i;
}
} else {
for( i=0; i<256; ++i ) {
WinVidPalette[i].peFlags = PC_RESERVED;
}
dwFlags |= DDPCAPS_ALLOW256;
}
if FAILED(DDraw->CreatePalette(dwFlags, WinVidPalette, &DDrawPalette, NULL))
throw ERR_CreatePalette;
if FAILED(PrimaryBufferSurface->SetPalette(DDrawPalette))
throw ERR_SetPalette;
}
void __cdecl CreateZBuffer() {
DDSDESC dsp;
if( (CurrentDisplayAdapter.D3DHWDeviceDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ZBUFFERLESSHSR) != 0 )
return;
memset(&dsp, 0, sizeof(dsp));
dsp.dwSize = sizeof(dsp);
dsp.dwWidth = GameVidBufWidth;
dsp.dwHeight = GameVidBufHeight;
dsp.dwFlags = DDSD_ZBUFFERBITDEPTH|DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS;
dsp.dwZBufferBitDepth = GetZBufferDepth();
dsp.ddsCaps.dwCaps = DDSCAPS_ZBUFFER|DDSCAPS_VIDEOMEMORY;
if FAILED(DDrawSurfaceCreate(&dsp, &ZBufferSurface))
throw ERR_CreateZBuffer;
if FAILED(BackBufferSurface->AddAttachedSurface(ZBufferSurface))
throw ERR_AttachZBuffer;
}
DWORD __cdecl GetZBufferDepth() {
DWORD bitDepthMask = CurrentDisplayAdapter.D3DHWDeviceDesc.dwDeviceZBufferBitDepth;
if( (bitDepthMask & DDBD_16) != 0 ) return 16;
if( (bitDepthMask & DDBD_24) != 0 ) return 24;
if( (bitDepthMask & DDBD_32) != 0 ) return 32;
return 8;
}
#endif // (DIRECT3D_VERSION >= 0x900)
void __cdecl CreateRenderBuffer() {
#if (DIRECT3D_VERSION >= 0x900)
SWRBufferFree(&RenderBuffer);
if( !SWRBufferCreate(&RenderBuffer, GameVidWidth, GameVidHeight) )
throw ERR_CreateRenderBuffer;
#else // (DIRECT3D_VERSION >= 0x900)
DDSDESC dsp;
memset(&dsp, 0, sizeof(dsp));
dsp.dwSize = sizeof(dsp);
dsp.dwFlags = DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS;
dsp.dwWidth = GameVidBufWidth;
dsp.dwHeight = GameVidBufHeight;
dsp.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY|DDSCAPS_OFFSCREENPLAIN;
if FAILED(DDrawSurfaceCreate(&dsp, &RenderBufferSurface))
throw ERR_CreateRenderBuffer;
if( !WinVidClearBuffer(RenderBufferSurface, NULL, 0) )
throw ERR_ClearRenderBuffer;
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __cdecl CreatePictureBuffer() {
#if (DIRECT3D_VERSION >= 0x900)
SWRBufferFree(&PictureBuffer);
if( !SWRBufferCreate(&PictureBuffer, BGND_PictureWidth, BGND_PictureHeight) )
throw ERR_CreatePictureBuffer;
#else // (DIRECT3D_VERSION >= 0x900)
DDSDESC dsp;
memset(&dsp, 0, sizeof(dsp));
dsp.dwSize = sizeof(dsp);
dsp.dwFlags = DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS;
dsp.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY|DDSCAPS_OFFSCREENPLAIN;
#ifdef FEATURE_BACKGROUND_IMPROVED
dsp.dwWidth = BGND_PictureWidth;
dsp.dwHeight = BGND_PictureHeight;
#else // !FEATURE_BACKGROUND_IMPROVED
dsp.dwWidth = 640;
dsp.dwHeight = 480;
#endif // FEATURE_BACKGROUND_IMPROVED
if FAILED(DDrawSurfaceCreate(&dsp, &PictureBufferSurface))
throw ERR_CreatePictureBuffer;
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __cdecl ClearBuffers(DWORD flags, DWORD fillColor) {
#if (DIRECT3D_VERSION >= 0x900)
if( CHK_ANY(flags, CLRB_RenderBuffer) )
SWRBufferClear(&RenderBuffer, 0);
if( CHK_ANY(flags, CLRB_PictureBuffer) )
SWRBufferClear(&PictureBuffer, 0);
#else // (DIRECT3D_VERSION >= 0x900)
DWORD d3dClearFlags = 0;
D3DRECT d3dRect;
RECT winRect;
if( (flags & CLRB_PhdWinSize) != 0 ) {
winRect.left = PhdWinMinX;
winRect.top = PhdWinMinY;
winRect.right = PhdWinMinX + PhdWinWidth;
winRect.bottom = PhdWinMinY + PhdWinHeight;
} else {
winRect.left = 0;
winRect.top = 0;
winRect.right = GameVidWidth;
winRect.bottom = GameVidHeight;
}
if( SavedAppSettings.RenderMode == RM_Hardware ) {
// Hardware Renderer Checks
if( (flags & CLRB_BackBuffer) != 0 )
d3dClearFlags |= D3DCLEAR_TARGET;
if( (flags & CLRB_ZBuffer) != 0 )
d3dClearFlags |= D3DCLEAR_ZBUFFER;
if( d3dClearFlags ) {
d3dRect.x1 = winRect.left;
d3dRect.y1 = winRect.top;
d3dRect.x2 = winRect.right;
d3dRect.y2 = winRect.bottom;
D3DView->Clear(1, &d3dRect, d3dClearFlags);
}
} else {
// Software Renderer Checks
if( (flags & CLRB_BackBuffer) != 0 )
WinVidClearBuffer(BackBufferSurface, &winRect, fillColor);
}
// Common checks
if( (flags & CLRB_PrimaryBuffer) != 0 ) {
WinVidClearBuffer(PrimaryBufferSurface, &winRect, fillColor);
#if defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED) || defined(FEATURE_VIDEOFX_IMPROVED)
if( CaptureBufferSurface != NULL ) {
WinVidClearBuffer(CaptureBufferSurface, &winRect, fillColor);
}
#endif // defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED) || defined(FEATURE_VIDEOFX_IMPROVED)
}
if( (flags & CLRB_ThirdBuffer) != 0 )
WinVidClearBuffer(ThirdBufferSurface, &winRect, fillColor);
if( (flags & CLRB_RenderBuffer) != 0 ) {
WinVidClearBuffer(RenderBufferSurface, &winRect, fillColor);
}
if( (flags & CLRB_PictureBuffer) != 0 ) {
winRect.left = 0;
winRect.top = 0;
#ifdef FEATURE_BACKGROUND_IMPROVED
winRect.right = BGND_PictureWidth;
winRect.bottom = BGND_PictureHeight;
#else // !FEATURE_BACKGROUND_IMPROVED
winRect.right = 640;
winRect.bottom = 480;
#endif // FEATURE_BACKGROUND_IMPROVED
WinVidClearBuffer(PictureBufferSurface, &winRect, fillColor);
}
if( (flags & CLRB_WindowedPrimaryBuffer) != 0 ) {
winRect.left = GameWindowPositionX;
winRect.top = GameWindowPositionY;
winRect.right = GameWindowPositionX + GameWindowWidth;
winRect.bottom = GameWindowPositionY + GameWindowHeight;
WinVidClearBuffer(PrimaryBufferSurface, &winRect, fillColor);
}
#endif // (DIRECT3D_VERSION >= 0x900)
}
#if (DIRECT3D_VERSION < 0x900)
void __cdecl RestoreLostBuffers() {
if( !PrimaryBufferSurface ) {
S_ExitSystem("Oops... no front buffer");
return; // the app is terminated here
}
if FAILED(DDrawSurfaceRestoreLost(PrimaryBufferSurface, NULL, SavedAppSettings.FullScreen)) {
goto REBUILD;
}
if( SavedAppSettings.FullScreen || SavedAppSettings.RenderMode == RM_Hardware ) {
if FAILED(DDrawSurfaceRestoreLost(BackBufferSurface, PrimaryBufferSurface, true))
goto REBUILD;
}
if( SavedAppSettings.TripleBuffering ) {
if FAILED(DDrawSurfaceRestoreLost(ThirdBufferSurface, PrimaryBufferSurface, true))
goto REBUILD;
}
if( SavedAppSettings.RenderMode == RM_Software ) {
if FAILED(DDrawSurfaceRestoreLost(RenderBufferSurface, NULL, false))
goto REBUILD;
}
if( ZBufferSurface ) {
if FAILED(DDrawSurfaceRestoreLost(ZBufferSurface, NULL, false))
goto REBUILD;
}
if( PictureBufferSurface ) {
if FAILED(DDrawSurfaceRestoreLost(PictureBufferSurface, NULL, false))
goto REBUILD;
}
#if defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED) || defined(FEATURE_VIDEOFX_IMPROVED)
if( CaptureBufferSurface ) {
if FAILED(DDrawSurfaceRestoreLost(CaptureBufferSurface, PrimaryBufferSurface, true))
goto REBUILD;
}
if( EnvmapBufferSurface ) {
if FAILED(DDrawSurfaceRestoreLost(EnvmapBufferSurface, NULL, false))
goto REBUILD;
}
#endif // defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED) || defined(FEATURE_VIDEOFX_IMPROVED)
return;
REBUILD:
if( !IsGameToExit ) {
ApplySettings(&SavedAppSettings);
if( SavedAppSettings.RenderMode == RM_Hardware ) {
HWR_GetPageHandles();
}
}
}
#endif // (DIRECT3D_VERSION < 0x900)
void __cdecl UpdateFrame(bool needRunMessageLoop, LPRECT rect) {
#if (DIRECT3D_VERSION >= 0x900)
// This method is pretty fast, so we can do it every frame
LPDDS backBuffer = NULL;
if SUCCEEDED(D3DDev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuffer)) {
if( SavedAppSettings.RenderMode == RM_Hardware ) {
D3DDev->StretchRect(backBuffer, NULL, CaptureBufferSurface, NULL, D3DTEXF_NONE);
#if FEATURE_VIDEOFX_IMPROVED
if( ReflectionMode != 0 ) SetEnvmapTexture(backBuffer);
#endif // FEATURE_VIDEOFX_IMPROVED
} else {
D3DDev->StretchRect(CaptureBufferSurface, NULL, backBuffer, NULL, D3DTEXF_NONE);
}
backBuffer->Release();
}
HRESULT res = D3DDev->Present(NULL, NULL, NULL, NULL);
if( res == D3DERR_DEVICELOST && !IsGameToExit ) {
FreeCaptureBuffer();
#if FEATURE_VIDEOFX_IMPROVED
FreeEnvmapTexture();
#endif // FEATURE_VIDEOFX_IMPROVED
D3DDeviceCreate(NULL);
if( SavedAppSettings.RenderMode == RM_Hardware ) {
HWR_InitState();
}
CreateCaptureBuffer();
}
D3DDev->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, 0, 1.0, 0);
#else // (DIRECT3D_VERSION >= 0x900)
RECT dstRect;
LPRECT pSrcRect = rect ? rect : &GameVidRect;
RestoreLostBuffers();
if( SavedAppSettings.FullScreen ) {
if( SavedAppSettings.RenderMode == RM_Software ) {
BackBufferSurface->Blt(pSrcRect, RenderBufferSurface, pSrcRect, DDBLT_WAIT, NULL);
}
PrimaryBufferSurface->Flip(NULL, DDFLIP_WAIT);
} else {
dstRect.left = GameWindowPositionX + pSrcRect->left;
dstRect.top = GameWindowPositionY + pSrcRect->top;
dstRect.right = GameWindowPositionX + pSrcRect->right;
dstRect.bottom = GameWindowPositionY + pSrcRect->bottom;
LPDDS surface = ( SavedAppSettings.RenderMode == RM_Software ) ? RenderBufferSurface : BackBufferSurface;
PrimaryBufferSurface->Blt(&dstRect, surface, pSrcRect, DDBLT_WAIT, NULL);
#if defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED) || defined(FEATURE_VIDEOFX_IMPROVED)
if( CaptureBufferSurface != NULL ) {
CaptureBufferSurface->Blt(pSrcRect, BackBufferSurface, pSrcRect, DDBLT_WAIT, NULL);
}
#endif // defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED) || defined(FEATURE_VIDEOFX_IMPROVED)
}
#endif // (DIRECT3D_VERSION >= 0x900)
if( needRunMessageLoop ) {
WinVidSpinMessageLoop(false);
}
}
#if (DIRECT3D_VERSION < 0x900)
void __cdecl WaitPrimaryBufferFlip() {
if( SavedAppSettings.FlipBroken && SavedAppSettings.FullScreen ) {
while( PrimaryBufferSurface->GetFlipStatus(DDGFS_ISFLIPDONE) == DDERR_WASSTILLDRAWING )
/* just wait */;
}
}
#endif // (DIRECT3D_VERSION < 0x900)
bool __cdecl RenderInit() {
return true;
}
void __cdecl RenderStart(bool isReset) {
int minWidth;
int minHeight;
DISPLAY_MODE dispMode;
#if (DIRECT3D_VERSION < 0x900)
bool is16bitTextures;
DDPIXELFORMAT pixelFormat;
if( isReset )
NeedToReloadTextures = false;
#endif // (DIRECT3D_VERSION < 0x900)
if( SavedAppSettings.FullScreen ) {
// FullScreen mode
if( SavedAppSettings.VideoMode == NULL )
throw ERR_GoFullScreen;
dispMode.width = SavedAppSettings.VideoMode->body.width;
dispMode.height = SavedAppSettings.VideoMode->body.height;
dispMode.bpp = SavedAppSettings.VideoMode->body.bpp;
dispMode.vga = SavedAppSettings.VideoMode->body.vga;
if( !WinVidGoFullScreen(&dispMode) )
throw ERR_GoFullScreen;
#if (DIRECT3D_VERSION < 0x900)
CreateScreenBuffers();
#endif // (DIRECT3D_VERSION < 0x900)
GameVidWidth = dispMode.width;
GameVidHeight = dispMode.height;
GameVidBPP = dispMode.bpp;
#if (DIRECT3D_VERSION < 0x900)
GameVidBufWidth = dispMode.width;
GameVidBufHeight = dispMode.height;
GameVid_IsVga = ( dispMode.vga != VGA_NoVga );
GameVid_IsWindowedVga = false;
GameVid_IsFullscreenVga = ( dispMode.vga == VGA_Standard );
#endif // (DIRECT3D_VERSION < 0x900)
} else {
// Windowed mode
minWidth = 320;
minHeight = CalculateWindowHeight(320, 200);
if( minHeight < 200 ) {
minWidth = CalculateWindowWidth(320, 200);
minHeight = 200;
}
WinVidSetMinWindowSize(minWidth, minHeight);
if( !WinVidGoWindowed(SavedAppSettings.WindowWidth, SavedAppSettings.WindowHeight, &dispMode) )
throw ERR_GoWindowed;
GameVidWidth = dispMode.width;
GameVidHeight = dispMode.height;
GameVidBPP = dispMode.bpp;
#if (DIRECT3D_VERSION < 0x900)
GameVidBufWidth = (dispMode.width + 0x1F) & ~0x1F;
GameVidBufHeight = (dispMode.height + 0x1F) & ~0x1F;
GameVid_IsVga = ( dispMode.vga != 0 );
GameVid_IsWindowedVga = ( dispMode.vga != VGA_NoVga );
GameVid_IsFullscreenVga = false;
CreatePrimarySurface();
if( SavedAppSettings.RenderMode == RM_Hardware ) {
CreateBackBuffer();
#if defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED) || defined(FEATURE_VIDEOFX_IMPROVED)
CreateCaptureBuffer();
#endif // defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED) || defined(FEATURE_VIDEOFX_IMPROVED)
}
CreateClipper();
#endif // (DIRECT3D_VERSION < 0x900)
}
#if (DIRECT3D_VERSION >= 0x900)
#if defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED)
FreeCaptureBuffer();
#endif // defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED)
#if FEATURE_VIDEOFX_IMPROVED
FreeEnvmapTexture();
#endif // FEATURE_VIDEOFX_IMPROVED
D3DDeviceCreate(NULL);
TextureFormat.bpp = 32;
#if defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED)
CreateCaptureBuffer();
#endif // defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED)
if( SavedAppSettings.RenderMode == RM_Hardware ) {
#if defined(FEATURE_BACKGROUND_IMPROVED)
BGND2_PrepareCaptureTextures();
#endif // defined(FEATURE_BACKGROUND_IMPROVED)
} else {
CreateRenderBuffer();
if( !PictureBuffer.bitmap ) {
CreatePictureBuffer();
}
}
#else // (DIRECT3D_VERSION >= 0x900)
memset(&pixelFormat, 0, sizeof(DDPIXELFORMAT));
pixelFormat.dwSize = sizeof(DDPIXELFORMAT);
if FAILED(PrimaryBufferSurface->GetPixelFormat(&pixelFormat))
throw ERR_GetPixelFormat;
WinVidGetColorBitMasks(&ColorBitMasks, &pixelFormat);
if( GameVid_IsVga )
CreateWindowPalette();
if( SavedAppSettings.RenderMode == RM_Hardware ) {
if( SavedAppSettings.ZBuffer ) {
CreateZBuffer();
}
D3DDeviceCreate(BackBufferSurface);
EnumerateTextureFormats();
#if defined(FEATURE_BACKGROUND_IMPROVED)
BGND2_PrepareCaptureTextures();
#endif // defined(FEATURE_BACKGROUND_IMPROVED)
} else {
CreateRenderBuffer();
if( PictureBufferSurface == NULL ) {
CreatePictureBuffer();
}
}
if( NeedToReloadTextures ) {
is16bitTextures = ( TextureFormat.bpp >= 16 );
if( IsWindowedVGA != GameVid_IsWindowedVga || Is16bitTextures != is16bitTextures ) {
S_ReloadLevelGraphics(IsWindowedVGA != GameVid_IsWindowedVga, Is16bitTextures != is16bitTextures );
IsWindowedVGA = GameVid_IsWindowedVga;
Is16bitTextures = is16bitTextures;
} else if( SavedAppSettings.RenderMode == RM_Hardware ) {
ReloadTextures(true);
HWR_GetPageHandles();
}
} else {
IsWindowedVGA = GameVid_IsWindowedVga;
Is16bitTextures = ( TextureFormat.bpp >= 16 );
}
GameVidBufRect.left = 0;
GameVidBufRect.top = 0;
GameVidBufRect.right = GameVidBufWidth;
GameVidBufRect.bottom = GameVidBufHeight;
#endif // (DIRECT3D_VERSION >= 0x900)
GameVidRect.left = 0;
GameVidRect.top = 0;
GameVidRect.right = GameVidWidth;
GameVidRect.bottom = GameVidHeight;
DumpWidth = GameVidWidth;
DumpHeight = GameVidHeight;
setup_screen_size();
#if (DIRECT3D_VERSION < 0x900)
NeedToReloadTextures = true;
#endif // (DIRECT3D_VERSION < 0x900)
}
void __cdecl RenderFinish(bool needToClearTextures) {
#if (DIRECT3D_VERSION >= 0x900)
S_DontDisplayPicture();
HWR_FreeTexturePages();
CleanupTextures();
SWRBufferFree(&PictureBuffer);
SWRBufferFree(&RenderBuffer);
FreeCaptureBuffer();
Direct3DRelease();
#else // (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.RenderMode == RM_Hardware ) {
// Hardware Renderer
if( needToClearTextures ) {
S_DontDisplayPicture(); // NOTE: this line was absent in the original game
HWR_FreeTexturePages();
CleanupTextures();
}
Direct3DRelease();
if( ZBufferSurface != NULL ) {
ZBufferSurface->Release();
ZBufferSurface = NULL;
}
} else {
// Software Renderer
if( needToClearTextures && PictureBufferSurface != NULL ) {
PictureBufferSurface->Release();
PictureBufferSurface = NULL;
}
if( RenderBufferSurface != NULL ) {
RenderBufferSurface->Release();
RenderBufferSurface = NULL;
}
}
if( DDrawPalette != NULL ) {
DDrawPalette->Release();
DDrawPalette = NULL;
}
if( DDrawClipper != NULL ) {
DDrawClipper->Release();
DDrawClipper = NULL;
}
if( ThirdBufferSurface != NULL ) {
ThirdBufferSurface->Release();
ThirdBufferSurface = NULL;
}
if( BackBufferSurface != NULL ) {
BackBufferSurface->Release();
BackBufferSurface = NULL;
}
#if defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED) || defined(FEATURE_VIDEOFX_IMPROVED)
if( CaptureBufferSurface != NULL ) {
CaptureBufferSurface->Release();
CaptureBufferSurface = NULL;
}
if( EnvmapBufferSurface != NULL ) {
EnvmapBufferSurface->Release();
EnvmapBufferSurface = NULL;
}
#endif // defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED) || defined(FEATURE_VIDEOFX_IMPROVED)
if( PrimaryBufferSurface != NULL ) {
PrimaryBufferSurface->Release();
PrimaryBufferSurface = NULL;
}
if( needToClearTextures )
NeedToReloadTextures = false;
#endif // (DIRECT3D_VERSION >= 0x900)
}
bool __cdecl ApplySettings(APP_SETTINGS *newSettings) {
char modeString[64] = {0};
APP_SETTINGS oldSettings = SavedAppSettings;
#if (DIRECT3D_VERSION < 0x900)
RenderFinish(false);
#endif // (DIRECT3D_VERSION < 0x900)
if( newSettings != &SavedAppSettings )
SavedAppSettings = *newSettings;
try {
RenderStart(false);
}
catch(...) {
#if (DIRECT3D_VERSION < 0x900)
RenderFinish(false);
#endif // (DIRECT3D_VERSION < 0x900)
SavedAppSettings = oldSettings;
try {
RenderStart(false);
}
catch(...) {
DISPLAY_MODE_LIST *modeList;
DISPLAY_MODE_NODE *mode;
DISPLAY_MODE targetMode;
#if (DIRECT3D_VERSION < 0x900)
RenderFinish(false);
#endif // (DIRECT3D_VERSION < 0x900)
SavedAppSettings.PreferredDisplayAdapter = PrimaryDisplayAdapter;
SavedAppSettings.RenderMode = RM_Software;
SavedAppSettings.FullScreen = true;
SavedAppSettings.TripleBuffering = false;
targetMode.width = 640;
targetMode.height = 480;
targetMode.bpp = 0;
#if (DIRECT3D_VERSION < 0x900)
targetMode.vga = VGA_NoVga;
#endif // (DIRECT3D_VERSION < 0x900)
modeList = &PrimaryDisplayAdapter->body.swDispModeList;
#ifdef FEATURE_NOLEGACY_OPTIONS
if( modeList->head ) {
targetMode.bpp = modeList->head->body.bpp;
#if (DIRECT3D_VERSION < 0x900)
targetMode.vga = modeList->head->body.vga;
#endif // (DIRECT3D_VERSION < 0x900)
}
#endif // FEATURE_NOLEGACY_OPTIONS
for( mode = modeList->head; mode; mode = mode->next ) {
if( !CompareVideoModes(&mode->body, &targetMode) )
break;
}
SavedAppSettings.VideoMode = mode ? mode : modeList->tail;
try {
RenderStart(false);
}
catch(...) {
S_ExitSystem("Can't reinitialise renderer");
return false; // the app is terminated here
}
}
}
S_InitialiseScreen(GFL_NOLEVEL);
if( SavedAppSettings.RenderMode != oldSettings.RenderMode ) {
S_ReloadLevelGraphics(1, 1);
}
#if (DIRECT3D_VERSION < 0x900)
else if( SavedAppSettings.RenderMode == RM_Software &&
SavedAppSettings.FullScreen != oldSettings.FullScreen )
{
S_ReloadLevelGraphics(1, 0);
}
#endif // (DIRECT3D_VERSION < 0x900)
#ifdef FEATURE_NOLEGACY_OPTIONS
snprintf(modeString, sizeof(modeString), "%dx%d", GameVidWidth, GameVidHeight);
#else // FEATURE_NOLEGACY_OPTIONS
if( SavedAppSettings.FullScreen )
sprintf(modeString, "%dx%dx%d", GameVidWidth, GameVidHeight, GameVidBPP);
else
sprintf(modeString, "%dx%d", GameVidWidth, GameVidHeight);
#endif // FEATURE_NOLEGACY_OPTIONS
DisplayModeInfo(modeString);
return true;
}
void __cdecl FmvBackToGame() {
try {
RenderStart(true);
}
catch(...) {
DISPLAY_MODE_LIST *modeList;
DISPLAY_MODE_NODE *mode;
DISPLAY_MODE targetMode;
#if (DIRECT3D_VERSION < 0x900)
RenderFinish(false);
#endif // (DIRECT3D_VERSION < 0x900)
SavedAppSettings.PreferredDisplayAdapter = PrimaryDisplayAdapter;
SavedAppSettings.RenderMode = RM_Software;
SavedAppSettings.FullScreen = true;
SavedAppSettings.TripleBuffering = false;
targetMode.width = 640;
targetMode.height = 480;
targetMode.bpp = 0;
#if (DIRECT3D_VERSION < 0x900)
targetMode.vga = VGA_NoVga;
#endif // (DIRECT3D_VERSION < 0x900)
modeList = &PrimaryDisplayAdapter->body.swDispModeList;
#ifdef FEATURE_NOLEGACY_OPTIONS
if( modeList->head ) {
targetMode.bpp = modeList->head->body.bpp;
#if (DIRECT3D_VERSION < 0x900)
targetMode.vga = modeList->head->body.vga;
#endif // (DIRECT3D_VERSION < 0x900)
}
#endif // FEATURE_NOLEGACY_OPTIONS
for( mode = modeList->head; mode; mode = mode->next ) {
if( !CompareVideoModes(&mode->body, &targetMode) )
break;
}
SavedAppSettings.VideoMode = mode ? mode : modeList->tail;
try {
RenderStart(false);
}
catch(...) {
S_ExitSystem("Can't reinitialise renderer");
return; // the app is terminated here
}
}
// NOTE: this HWR init was absent in the original code, but must be done here
if( SavedAppSettings.RenderMode == RM_Hardware ) {
HWR_InitState();
}
}
void __cdecl GameApplySettings(APP_SETTINGS *newSettings) {
bool needInitRenderState = false;
bool needRebuildBuffers = false;
bool needAdjustTexel = false;
DISPLAY_MODE dispMode;
if( newSettings->PreferredDisplayAdapter != SavedAppSettings.PreferredDisplayAdapter||
newSettings->PreferredSoundAdapter != SavedAppSettings.PreferredSoundAdapter ||
newSettings->PreferredJoystick != SavedAppSettings.PreferredJoystick )
{
return;
}
#if (DIRECT3D_VERSION >= 0x900)
if( newSettings->ZBuffer != SavedAppSettings.ZBuffer ||
newSettings->BilinearFiltering != SavedAppSettings.BilinearFiltering )
{
char msg[32] = {0};
if( newSettings->ZBuffer != SavedAppSettings.ZBuffer ) {
snprintf(msg, sizeof(msg), "Z Buffer: %s",
newSettings->ZBuffer ? GF_SpecificStringTable[SSI_On] : GF_SpecificStringTable[SSI_Off]);
} else if( newSettings->BilinearFiltering != SavedAppSettings.BilinearFiltering ) {
snprintf(msg, sizeof(msg), "Bilinear Filter: %s",
newSettings->BilinearFiltering ? GF_SpecificStringTable[SSI_On] : GF_SpecificStringTable[SSI_Off]);
}
if( *msg ) DisplayModeInfo(msg);
needInitRenderState = true;
}
#else // (DIRECT3D_VERSION >= 0x900)
if( newSettings->PerspectiveCorrect != SavedAppSettings.PerspectiveCorrect ||
newSettings->Dither != SavedAppSettings.Dither ||
newSettings->BilinearFiltering != SavedAppSettings.BilinearFiltering )
{
needInitRenderState = true;
}
#endif // (DIRECT3D_VERSION >= 0x900)
if( newSettings->BilinearFiltering != SavedAppSettings.BilinearFiltering ) {
needAdjustTexel = true;
}
#if (DIRECT3D_VERSION >= 0x900)
if( newSettings->RenderMode != SavedAppSettings.RenderMode ||
newSettings->VideoMode != SavedAppSettings.VideoMode ||
newSettings->FullScreen != SavedAppSettings.FullScreen )
#else // (DIRECT3D_VERSION >= 0x900)
if( newSettings->RenderMode != SavedAppSettings.RenderMode ||
newSettings->VideoMode != SavedAppSettings.VideoMode ||
newSettings->FullScreen != SavedAppSettings.FullScreen ||
newSettings->ZBuffer != SavedAppSettings.ZBuffer ||
newSettings->TripleBuffering != SavedAppSettings.TripleBuffering )
#endif // (DIRECT3D_VERSION >= 0x900)
{
ApplySettings(newSettings);
S_AdjustTexelCoordinates();
return;
}
#ifdef FEATURE_VIDEOFX_IMPROVED
if( newSettings->LightingMode != SavedAppSettings.LightingMode ) {
const char *levels[3] = {
"Low",
"Medium",
"High",
};
char msg[32] = {0};
snprintf(msg, sizeof(msg), "Lighting Contrast: %s", levels[newSettings->LightingMode]);
DisplayModeInfo(msg);
needInitRenderState = true;
}
#endif // FEATURE_VIDEOFX_IMPROVED
if( !newSettings->FullScreen ) {
if( newSettings->WindowWidth != SavedAppSettings.WindowWidth || newSettings->WindowHeight != SavedAppSettings.WindowHeight ) {
if( !WinVidGoWindowed(newSettings->WindowWidth, newSettings->WindowHeight, &dispMode) ) {
return;
}
newSettings->WindowWidth = dispMode.width;
newSettings->WindowHeight = dispMode.height;
if( dispMode.width != SavedAppSettings.WindowWidth || dispMode.height != SavedAppSettings.WindowHeight ) {
#if (DIRECT3D_VERSION >= 0x900)
needRebuildBuffers = true;
#else // (DIRECT3D_VERSION >= 0x900)
if( GameVidBufWidth < dispMode.width || GameVidBufWidth - dispMode.width > 0x40 ||
GameVidBufHeight < dispMode.height || GameVidBufHeight - dispMode.height > 0x40 )
{
needRebuildBuffers = true;
} else {
SavedAppSettings.WindowWidth = dispMode.width;
SavedAppSettings.WindowHeight = dispMode.height;
GameVidWidth = dispMode.width;
GameVidHeight = dispMode.height;
GameVidRect.right = dispMode.width;
GameVidRect.bottom = dispMode.height;
if( SavedAppSettings.RenderMode == RM_Hardware ) {
D3DSetViewport();
}
setup_screen_size();
WinVidNeedToResetBuffers = false;
}
#endif // (DIRECT3D_VERSION >= 0x900)
}
}
}
if( needInitRenderState ) {
#if (DIRECT3D_VERSION >= 0x900)
SavedAppSettings.ZBuffer = newSettings->ZBuffer;
SavedAppSettings.BilinearFiltering = newSettings->BilinearFiltering;
setup_screen_size();
#else // (DIRECT3D_VERSION >= 0x900)
SavedAppSettings.PerspectiveCorrect = newSettings->PerspectiveCorrect;
SavedAppSettings.Dither = newSettings->Dither;
SavedAppSettings.BilinearFiltering = newSettings->BilinearFiltering;
#endif // (DIRECT3D_VERSION >= 0x900)
#ifdef FEATURE_VIDEOFX_IMPROVED
SavedAppSettings.LightingMode = newSettings->LightingMode;
if( SavedAppSettings.RenderMode == RM_Software ) {
extern void UpdateDepthQ(bool isReset);
UpdateDepthQ(false);
}
#endif // FEATURE_VIDEOFX_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware ) {
HWR_InitState();
}
}
if( needRebuildBuffers ) {
ClearBuffers(CLRB_WindowedPrimaryBuffer, 0);
ApplySettings(newSettings);
}
if( needAdjustTexel ) {
S_AdjustTexelCoordinates();
}
}
void __cdecl UpdateGameResolution() {
APP_SETTINGS newSettings = SavedAppSettings;
char modeString[64] = {0};
newSettings.WindowWidth = GameWindowWidth;
newSettings.WindowHeight = GameWindowHeight;
GameApplySettings(&newSettings);
sprintf(modeString, "%dx%d", GameVidWidth, GameVidHeight);
DisplayModeInfo(modeString);
}
LPCTSTR __cdecl DecodeErrorMessage(DWORD errorCode) {
return ErrorStringTable[errorCode];
}
/*
* Inject function
*/
void Inject_InitDisplay() {
#if (DIRECT3D_VERSION < 0x900)
INJECT(0x004484E0, CreateScreenBuffers);
INJECT(0x00448620, CreatePrimarySurface);
INJECT(0x004486C0, CreateBackBuffer);
INJECT(0x00448760, CreateClipper);
INJECT(0x00448800, CreateWindowPalette);
INJECT(0x004488E0, CreateZBuffer);
INJECT(0x004489A0, GetZBufferDepth);
#endif // (DIRECT3D_VERSION < 0x900)
INJECT(0x004489D0, CreateRenderBuffer);
INJECT(0x00448A80, CreatePictureBuffer);
INJECT(0x00448AF0, ClearBuffers);
#if (DIRECT3D_VERSION < 0x900)
INJECT(0x00448CA0, RestoreLostBuffers);
#endif // (DIRECT3D_VERSION < 0x900)
INJECT(0x00448DE0, UpdateFrame);
#if (DIRECT3D_VERSION < 0x900)
INJECT(0x00448EB0, WaitPrimaryBufferFlip);
#endif // (DIRECT3D_VERSION < 0x900)
INJECT(0x00448EF0, RenderInit);
INJECT(0x00448F00, RenderStart);
INJECT(0x004492B0, RenderFinish);
INJECT(0x004493A0, ApplySettings);
INJECT(0x004495B0, FmvBackToGame);
INJECT(0x004496C0, GameApplySettings);
INJECT(0x00449900, UpdateGameResolution);
INJECT(0x00449970, DecodeErrorMessage);
}
================================================
FILE: specific/init_display.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef INIT_DISPLAY_H_INCLUDED
#define INIT_DISPLAY_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#if (DIRECT3D_VERSION < 0x900)
void __cdecl CreateScreenBuffers(); // 0x004484E0
void __cdecl CreatePrimarySurface(); // 0x00448620
void __cdecl CreateBackBuffer(); // 0x004486C0
void __cdecl CreateClipper(); // 0x00448760
void __cdecl CreateWindowPalette(); // 0x00448800
void __cdecl CreateZBuffer(); // 0x004488E0
DWORD __cdecl GetZBufferDepth(); // 0x004489A0
#endif // (DIRECT3D_VERSION < 0x900)
void __cdecl CreateRenderBuffer(); // 0x004489D0
void __cdecl CreatePictureBuffer(); // 0x00448A80
void __cdecl ClearBuffers(DWORD flags, DWORD fillColor); // 0x00448AF0
#if (DIRECT3D_VERSION < 0x900)
void __cdecl RestoreLostBuffers(); // 0x00448CA0
#endif // (DIRECT3D_VERSION < 0x900)
void __cdecl UpdateFrame(bool needRunMessageLoop, LPRECT rect); // 0x00448DE0
#if (DIRECT3D_VERSION < 0x900)
void __cdecl WaitPrimaryBufferFlip(); // 0x00448EB0
#endif // (DIRECT3D_VERSION < 0x900)
bool __cdecl RenderInit(); // 0x00448EF0
void __cdecl RenderStart(bool isReset); // 0x00448F00
void __cdecl RenderFinish(bool needToClearTextures); // 0x004492B0
bool __cdecl ApplySettings(APP_SETTINGS *newSettings); // 0x004493A0
void __cdecl FmvBackToGame(); // 0x004495B0
void __cdecl GameApplySettings(APP_SETTINGS *newSettings); // 0x004496C0
void __cdecl UpdateGameResolution(); // 0x00449900
LPCTSTR __cdecl DecodeErrorMessage(DWORD errorCode); // 0x00449970
#endif // INIT_DISPLAY_H_INCLUDED
================================================
FILE: specific/init_input.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/init_input.h"
#include "3dsystem/phd_math.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/xinput_ex.h"
#include "modding/raw_input.h"
#include "modding/joy_output.h"
#define HAS_AXIS(x) (JoyRanges[x].lMax != JoyRanges[x].lMin)
typedef enum {
JoyX,
JoyY,
JoyZ,
JoyRX,
JoyRY,
JoyRZ,
JoyAxisNumber,
} JOY_AXIS_ENUM;
typedef struct {
long lMin;
long lMax;
} JOY_AXIS_RANGE;
static bool IsRawInput = false;
static int XInputIndex = -1;
static XINPUT_CAPABILITIES XInputCaps;
static DIDEVCAPS JoyCaps;
static JOY_AXIS_RANGE JoyRanges[JoyAxisNumber];
bool JoystickVibrationEnabled = true;
bool JoystickLedColorEnabled = true;
BOOL CALLBACK DInputEnumJoystickAxisCallback(LPCDIDEVICEOBJECTINSTANCE pdidoi, LPVOID pContext) {
if( !pdidoi || !pContext ) {
return DIENUM_CONTINUE;
}
DIPROPRANGE prop;
prop.diph.dwSize = sizeof(prop);
prop.diph.dwHeaderSize = sizeof(prop.diph);
prop.diph.dwHow = DIPH_BYOFFSET;
prop.diph.dwObj = pdidoi->dwOfs;
if( FAILED( IDID_SysJoystick->GetProperty(DIPROP_RANGE, &prop.diph) ) ) {
return DIENUM_CONTINUE;
}
DWORD axisOfs[JoyAxisNumber] = {
DIJOFS_X, DIJOFS_Y, DIJOFS_Z,
DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ,
};
JOY_AXIS_RANGE *axisRanges = (JOY_AXIS_RANGE *)pContext;
for( int i=0; idwOfs == axisOfs[i] ) {
axisRanges[i].lMin = prop.lMin;
axisRanges[i].lMax = prop.lMax;
break;
}
}
return DIENUM_CONTINUE;
}
static BOOL CALLBACK RawInputCallBack(LPGUID lpGuid, LPCTSTR lpDeviceName, LPCTSTR lpProductName, WORD vid, WORD pid, LPVOID lpContext) {
if( lpGuid == NULL || lpDeviceName == NULL || lpProductName == NULL || lpContext == NULL || IsXInputDevice(vid, pid) )
return TRUE;
JOYSTICK_LIST *joyList = (JOYSTICK_LIST *)lpContext;
JOYSTICK_NODE *joyNode = new JOYSTICK_NODE;
if( joyNode == NULL )
return TRUE;
joyNode->next = NULL;
joyNode->previous = joyList->tail;
if( !joyList->head )
joyList->head = joyNode;
if( joyList->tail )
joyList->tail->next = joyNode;
joyList->tail = joyNode;
joyList->dwCount++;
joyNode->body.joystickGuid = *lpGuid;
joyNode->body.lpJoystickGuid = &joyNode->body.joystickGuid;
FlaggedStringCreate(&joyNode->body.productName, strlen(lpProductName)+1);
FlaggedStringCreate(&joyNode->body.instanceName, strlen(lpDeviceName)+1);
lstrcpy(joyNode->body.productName.lpString, lpProductName);
lstrcpy(joyNode->body.instanceName.lpString, lpDeviceName);
joyNode->body.iface = JOY_RawInput;
return TRUE;
}
void SetJoystickOutput(WORD leftMotor, WORD rightMotor, DWORD ledColor) {
static DWORD leftMotorOld = ~0;
static DWORD rightMotorOld = ~0;
static DWORD ledColorOld = ~0;
if( !SavedAppSettings.JoystickEnabled
|| (leftMotor == leftMotorOld && rightMotor == rightMotorOld && ledColor == ledColorOld) )
{
return;
}
bool result = true;
if( XInputIndex >= 0 ) {
XINPUT_VIBRATION vibration;
vibration.wLeftMotorSpeed = leftMotor;
vibration.wRightMotorSpeed = rightMotor;
XInputEnable(TRUE);
result = (ERROR_SUCCESS == XInputSetState(XInputIndex, &vibration));
} else if( IsRawInput ) {
result = RawInputSetState(leftMotor, rightMotor, ledColor);
}
if( result ) {
leftMotorOld = leftMotor;
rightMotorOld = rightMotor;
ledColorOld = ledColor;
}
}
JOYTYPE GetJoystickType() {
if( SavedAppSettings.JoystickEnabled && SavedAppSettings.PreferredJoystick ) {
if( IsRawInput ) {
return JT_PLAYSTATION;
}
if( XInputIndex >= 0 ) {
return JT_XINPUT;
}
if( IDID_SysJoystick != NULL ) {
return JT_DIRECTINPUT;
}
}
return JT_NONE;
}
bool IsJoyVibrationSupported() {
if( IsRawInput ) {
return true;
}
if( XInputIndex >= 0 ) {
return( XInputCaps.Vibration.wLeftMotorSpeed || XInputCaps.Vibration.wLeftMotorSpeed );
}
return false;
}
bool IsJoyLedColorSupported() {
return IsRawInput;
}
bool IsJoyVibrationEnabled() {
return SavedAppSettings.JoystickEnabled && JoystickVibrationEnabled && IsJoyVibrationSupported();
}
bool IsJoyLedColorEnabled() {
return SavedAppSettings.JoystickEnabled && JoystickLedColorEnabled && IsJoyLedColorSupported();
}
#endif // FEATURE_INPUT_IMPROVED
extern void __thiscall FlaggedStringDelete(STRING_FLAGGED *item);
extern bool FlaggedStringCopy(STRING_FLAGGED *dst, STRING_FLAGGED *src);
bool __cdecl DInputCreate() {
#if (DIRECTINPUT_VERSION >= 0x800)
return SUCCEEDED(DirectInput8Create(GameModule, DIRECTINPUT_VERSION, IID_IDirectInput8, (LPVOID *)&DInput, NULL));
#else // (DIRECTINPUT_VERSION >= 0x800)
return SUCCEEDED(DirectInputCreate(GameModule, DIRECTINPUT_VERSION, &DInput, NULL));
#endif // (DIRECTINPUT_VERSION >= 0x800)
}
void __cdecl DInputRelease() {
if( DInput != NULL ) {
DInput->Release();
DInput = NULL;
}
}
void __cdecl WinInReadKeyboard(LPVOID lpInputData) {
// NOTE: the original code did check only DIERR_INPUTLOST. Any FAILED state must be check to call Acquire
while FAILED(IDID_SysKeyboard->GetDeviceState(256, lpInputData)) {
if FAILED(IDID_SysKeyboard->Acquire()) {
memset(lpInputData, 0, 256);
break;
}
}
}
#ifdef FEATURE_INPUT_IMPROVED
static void PovToPos(int *xPos, int *yPos, DWORD pov) {
int x=0, y=0;
// Check if D-PAD is not centered
if( LOWORD(pov) != 0xFFFF ) {
// We need here cyclic quadrilateral with a side 32.
// The radius of circumscribed circle is approx 23 = 16 * SQRT(2)
x = +23 * phd_sin(pov * PHD_360 / 36000) / PHD_IONE;
y = -23 * phd_cos(pov * PHD_360 / 36000) / PHD_IONE;
CLAMP(x, -16, 16);
CLAMP(y, -16, 16);
}
if( xPos ) *xPos = x;
if( yPos ) *yPos = y;
}
static int SelectJoyDirection(int dp, int ls, int rs, int threshold) {
// D-Pad has priority
if( ABS(dp) > threshold ) return dp;
// if sticks have opposite directions, just sum them
if( (ls^rs) < 0 ) return ls + rs;
// or just select the one with max value
if( ABS(rs) > ABS(ls) ) return rs;
return ls;
}
static DWORD XInputReadJoystick(int *xPos, int *yPos) {
if( !xPos || !yPos || XInputIndex < 0 ) return 0;
*xPos = *yPos = 0;
DWORD buttonStatus = 0;
XINPUT_STATE state;
XInputEnable(TRUE);
if( ERROR_SUCCESS != XInputGetState(XInputIndex, &state) ) {
return 0;
}
int dx=0, dy=0, lx=0, ly=0, rx=0, ry=0;
if( XINPUT_DPAD(XInputCaps.Gamepad.wButtons) ) {
dx += CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT) ? 16 : 0;
dx -= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT) ? 16 : 0;
dy += CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN) ? 16 : 0;
dy -= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP) ? 16 : 0;
}
if( XInputCaps.Gamepad.sThumbLX && XInputCaps.Gamepad.sThumbLY ) {
lx = 16 * state.Gamepad.sThumbLX / 0x8000;
ly = -16 * state.Gamepad.sThumbLY / 0x8000;
}
if( XInputCaps.Gamepad.sThumbRX && XInputCaps.Gamepad.sThumbRY ) {
rx = 16 * state.Gamepad.sThumbRX / 0x8000;
ry = -16 * state.Gamepad.sThumbRY / 0x8000;
}
*xPos = SelectJoyDirection(dx, lx, rx, 8);
*yPos = SelectJoyDirection(dy, ly, ry, 8);
buttonStatus |= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_Y) ? 0x001 : 0;
buttonStatus |= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_B) ? 0x002 : 0;
buttonStatus |= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_A) ? 0x004 : 0;
buttonStatus |= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_X) ? 0x008 : 0;
buttonStatus |= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER) ? 0x010 : 0;
buttonStatus |= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 0x020 : 0;
buttonStatus |= (state.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) ? 0x040 : 0;
buttonStatus |= (state.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) ? 0x080 : 0;
buttonStatus |= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_BACK) ? 0x100 : 0;
buttonStatus |= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_START) ? 0x200 : 0;
buttonStatus |= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB) ? 0x400 : 0;
buttonStatus |= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB) ? 0x800 : 0;
buttonStatus |= CHK_ANY(state.Gamepad.wButtons, XINPUT_GAMEPAD_GUIDE) ? 0x1000 : 0;
return buttonStatus;
}
static DWORD RawInputReadJoystick(int *xPos, int *yPos) {
if( !xPos || !yPos || !IsRawInput ) return 0;
*xPos = *yPos = 0;
DWORD buttonStatus = 0;
RINPUT_STATE state;
if( !RawInputGetState(&state) ) {
return 0;
}
int dx=0, dy=0, lx=0, ly=0, rx=0, ry=0;
PovToPos(&dx, &dy, state.dPad);
lx = (int)(+16.0 * state.axisLX);
ly = (int)(-16.0 * state.axisLY);
rx = (int)(+16.0 * state.axisRX);
ry = (int)(-16.0 * state.axisRY);
*xPos = SelectJoyDirection(dx, lx, rx, 8);
*yPos = SelectJoyDirection(dy, ly, ry, 8);
buttonStatus |= state.btnTriangle ? 0x0001 : 0;
buttonStatus |= state.btnCircle ? 0x0002 : 0;
buttonStatus |= state.btnCross ? 0x0004 : 0;
buttonStatus |= state.btnSquare ? 0x0008 : 0;
buttonStatus |= state.btnL1 ? 0x0010 : 0;
buttonStatus |= state.btnR1 ? 0x0020 : 0;
buttonStatus |= state.btnL2 ? 0x0040 : 0;
buttonStatus |= state.btnR2 ? 0x0080 : 0;
buttonStatus |= state.btnShare ? 0x0100 : 0;
buttonStatus |= state.btnOptions ? 0x0200 : 0;
buttonStatus |= state.btnL3 ? 0x0400 : 0;
buttonStatus |= state.btnR3 ? 0x0800 : 0;
buttonStatus |= state.btnPS ? 0x1000 : 0;
buttonStatus |= state.btnTouch ? 0x2000 : 0;
return buttonStatus;
}
static DWORD DInputReadJoystick(int *xPos, int *yPos) {
if( !xPos || !yPos || !IDID_SysJoystick ) return 0;
*xPos = *yPos = 0;
DWORD buttonStatus = 0;
DIJOYSTATE joyState;
#if (DIRECTINPUT_VERSION >= 0x800)
while( FAILED(IDID_SysJoystick->Poll()) || FAILED(IDID_SysJoystick->GetDeviceState(sizeof(joyState), &joyState)) )
#else // (DIRECTINPUT_VERSION >= 0x800)
while( FAILED(IDID_SysJoystick->GetDeviceState(sizeof(joyState), &joyState)) )
#endif // (DIRECTINPUT_VERSION >= 0x800)
{
if FAILED(IDID_SysJoystick->Acquire()) return 0;
}
int dx=0, dy=0, lx=0, ly=0, rx=0, ry=0;
if( JoyCaps.dwPOVs ) {
PovToPos(&dx, &dy, joyState.rgdwPOV[0]);
}
if( HAS_AXIS(JoyX) && HAS_AXIS(JoyY) ) {
lx = 32 * joyState.lX / (JoyRanges[JoyX].lMax - JoyRanges[JoyX].lMin) - 16;
ly = 32 * joyState.lY / (JoyRanges[JoyY].lMax - JoyRanges[JoyY].lMin) - 16;
}
if( HAS_AXIS(JoyZ) && HAS_AXIS(JoyRZ) ) {
rx = 32 * joyState.lZ / (JoyRanges[JoyZ].lMax - JoyRanges[JoyZ].lMin) - 16;
ry = 32 * joyState.lRz / (JoyRanges[JoyRZ].lMax - JoyRanges[JoyRZ].lMin) - 16;
} else if( HAS_AXIS(JoyRX) && HAS_AXIS(JoyRY) ) {
rx = 32 * joyState.lRx / (JoyRanges[JoyRX].lMax - JoyRanges[JoyRX].lMin) - 16;
ry = 32 * joyState.lRy / (JoyRanges[JoyRY].lMax - JoyRanges[JoyRY].lMin) - 16;
}
*xPos = SelectJoyDirection(dx, lx, rx, 8);
*yPos = SelectJoyDirection(dy, ly, ry, 8);
for( DWORD i=0; inext;
FlaggedStringDelete(&node->body.productName);
FlaggedStringDelete(&node->body.instanceName);
delete(node);
}
JoystickList.head = NULL;
JoystickList.tail = NULL;
JoystickList.dwCount = 0;
if( !DInputCreate() )
return false;
result = DInputEnumDevices(&JoystickList);
DInputRelease();
return result;
}
bool __cdecl DInputEnumDevices(JOYSTICK_LIST *joystickList) {
#ifdef FEATURE_INPUT_IMPROVED
for( DWORD i = 0; i < XUSER_MAX_COUNT; ++i ) {
XINPUT_STATE state;
memset(&state, 0, sizeof(state));
if( ERROR_SUCCESS != XInputGetState(i, &state) ) {
continue;
}
JOYSTICK_NODE *joyNode = new JOYSTICK_NODE;
if( joyNode == NULL ) {
continue;
}
joyNode->next = NULL;
joyNode->previous = joystickList->tail;
if( !joystickList->head ) {
joystickList->head = joyNode;
}
if( joystickList->tail ) {
joystickList->tail->next = joyNode;
}
joystickList->tail = joyNode;
joystickList->dwCount++;
memset(&joyNode->body.joystickGuid, 0, sizeof(GUID));
joyNode->body.joystickGuid.Data4[7] = i;
joyNode->body.lpJoystickGuid = &joyNode->body.joystickGuid;
FlaggedStringCreate(&joyNode->body.productName, 256);
FlaggedStringCreate(&joyNode->body.instanceName, 256);
snprintf(joyNode->body.productName.lpString, 256, "XInput Controller %lu", i+1);
joyNode->body.iface = JOY_XInput;
}
RawInputEnumerate(RawInputCallBack, (LPVOID)joystickList);
#endif // FEATURE_INPUT_IMPROVED
return SUCCEEDED(DInput->EnumDevices(DIDEVTYPE_JOYSTICK, DInputEnumDevicesCallback, (LPVOID)joystickList, DIEDFL_ATTACHEDONLY));
}
BOOL CALLBACK DInputEnumDevicesCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) {
if( lpddi == NULL || pvRef == NULL ) {
return DIENUM_CONTINUE;
}
#ifdef FEATURE_INPUT_IMPROVED
DWORD vid = LOWORD(lpddi->guidProduct.Data1);
DWORD pid = HIWORD(lpddi->guidProduct.Data1);
if( GetRawInputName(vid, pid, FALSE) || IsXInputDevice(vid, pid) ) {
return DIENUM_CONTINUE;
}
#endif // FEATURE_INPUT_IMPROVED
JOYSTICK_LIST *joyList = (JOYSTICK_LIST *)pvRef;
JOYSTICK_NODE *joyNode = new JOYSTICK_NODE;
if( joyNode == NULL )
return DIENUM_CONTINUE;
joyNode->next = NULL;
joyNode->previous = joyList->tail;
if( !joyList->head )
joyList->head = joyNode;
if( joyList->tail )
joyList->tail->next = joyNode;
joyList->tail = joyNode;
joyList->dwCount++;
joyNode->body.joystickGuid = lpddi->guidInstance;
joyNode->body.lpJoystickGuid = &joyNode->body.joystickGuid;
FlaggedStringCreate(&joyNode->body.productName, 256);
FlaggedStringCreate(&joyNode->body.instanceName, 256);
lstrcpy(joyNode->body.productName.lpString, lpddi->tszProductName);
lstrcpy(joyNode->body.instanceName.lpString, lpddi->tszInstanceName);
#ifdef FEATURE_INPUT_IMPROVED
joyNode->body.iface = JOY_DirectInput;
#endif // FEATURE_INPUT_IMPROVED
return DIENUM_CONTINUE;
}
void __thiscall FlaggedStringCreate(STRING_FLAGGED *item, DWORD dwSize) {
item->lpString = new char[dwSize];
if( item->lpString != NULL ) {
*item->lpString = 0;
item->isPresented = true;
}
}
JOYSTICK_NODE *__cdecl GetJoystick(GUID *lpGuid) {
JOYSTICK_NODE *joystick;
if( JoystickList.dwCount == 0 )
return NULL;
if( lpGuid != NULL ) {
for( joystick = JoystickList.head; joystick; joystick = joystick->next ) {
if( !memcmp(&joystick->body.joystickGuid, lpGuid, sizeof(GUID)) )
return joystick;
}
}
return JoystickList.head;
}
void __cdecl DInputKeyboardCreate() {
if FAILED(DInput->CreateDevice(GUID_SysKeyboard, &IDID_SysKeyboard, NULL))
throw ERR_CantCreateKeyboardDevice;
if FAILED(IDID_SysKeyboard->SetCooperativeLevel(HGameWindow, DISCL_FOREGROUND|DISCL_NONEXCLUSIVE))
throw ERR_CantSetKBCooperativeLevel;
if FAILED(IDID_SysKeyboard->SetDataFormat(&c_dfDIKeyboard))
throw ERR_CantSetKBDataFormat;
// NOTE: there is no DIERR_OTHERAPPHASPRIO check in the original code
HRESULT res = IDID_SysKeyboard->Acquire();
if( !SUCCEEDED(res) && res != DIERR_OTHERAPPHASPRIO )
throw ERR_CantAcquireKeyboard;
}
void __cdecl DInputKeyboardRelease() {
if( IDID_SysKeyboard != NULL ) {
IDID_SysKeyboard->Unacquire();
IDID_SysKeyboard->Release();
IDID_SysKeyboard = NULL;
}
}
bool __cdecl DInputJoystickCreate() {
if( SavedAppSettings.PreferredJoystick == NULL )
return true;
JOYSTICK *preferred = &SavedAppSettings.PreferredJoystick->body;
CurrentJoystick = *preferred;
FlaggedStringCopy(&CurrentJoystick.productName, &preferred->productName);
FlaggedStringCopy(&CurrentJoystick.instanceName, &preferred->instanceName);
#ifdef FEATURE_INPUT_IMPROVED
XInputIndex = -1;
memset(&XInputCaps, 0, sizeof(XInputCaps));
GUID *guid = &CurrentJoystick.joystickGuid;
if( CurrentJoystick.iface == JOY_XInput ) {
if( ERROR_SUCCESS != XInputGetCapabilities(guid->Data4[7], 0, &XInputCaps) ) {
return false;
}
XInputIndex = guid->Data4[7];
return true;
}
if( CurrentJoystick.iface == JOY_RawInput ) {
IsRawInput = RawInputStart(CurrentJoystick.instanceName.lpString);
return IsRawInput;
}
memset(JoyRanges, 0, sizeof(JoyRanges));
memset(&JoyCaps, 0, sizeof(JoyCaps));
JoyCaps.dwSize = sizeof(JoyCaps);
if FAILED(DInput->CreateDevice(CurrentJoystick.joystickGuid, &IDID_SysJoystick, NULL))
return false;
if FAILED(IDID_SysJoystick->SetCooperativeLevel(HGameWindow, DISCL_FOREGROUND|DISCL_NONEXCLUSIVE))
return false;
if FAILED(IDID_SysJoystick->SetDataFormat(&c_dfDIJoystick))
return false;
if FAILED(IDID_SysJoystick->GetCapabilities(&JoyCaps))
return false;
if FAILED(IDID_SysJoystick->EnumObjects(DInputEnumJoystickAxisCallback, (LPVOID)JoyRanges, DIDFT_AXIS))
return false;
HRESULT res = IDID_SysJoystick->Acquire();
if( !SUCCEEDED(res) && res != DIERR_OTHERAPPHASPRIO )
return false;
#endif // FEATURE_INPUT_IMPROVED
return true;
}
void __cdecl DInputJoystickRelease() {
#ifdef FEATURE_INPUT_IMPROVED
SetJoystickOutput(0, 0, DEFAULT_JOYSTICK_LED_COLOR);
RawInputStop();
IsRawInput = false;
XInputEnable(FALSE);
XInputIndex = -1;
#endif // FEATURE_INPUT_IMPROVED
if( IDID_SysJoystick != NULL ) {
IDID_SysJoystick->Unacquire();
IDID_SysJoystick->Release();
IDID_SysJoystick = NULL;
}
}
void __cdecl WinInStart() {
if( !DInputCreate() )
throw ERR_CantCreateDirectInput;
DInputKeyboardCreate();
DInputJoystickCreate();
}
void __cdecl WinInFinish() {
DInputJoystickRelease();
DInputKeyboardRelease();
DInputRelease();
}
void __cdecl WinInRunControlPanel(HWND hWnd) {
if( DInput != NULL ) {
#ifdef FEATURE_INPUT_IMPROVED
JOYSTICK *preferred = &ChangedAppSettings.PreferredJoystick->body;
if SUCCEEDED(DInput->CreateDevice(preferred->joystickGuid, &IDID_SysJoystick, NULL))
{
IDID_SysJoystick->RunControlPanel(hWnd, 0);
IDID_SysJoystick->Release();
IDID_SysJoystick = NULL;
return;
}
#endif // FEATURE_INPUT_IMPROVED
DInput->RunControlPanel(hWnd, 0);
}
}
/*
* Inject function
*/
void Inject_InitInput() {
INJECT(0x004472A0, DInputCreate);
INJECT(0x004472D0, DInputRelease);
INJECT(0x004472F0, WinInReadKeyboard);
INJECT(0x00447350, WinInReadJoystick);
INJECT(0x00447460, WinInputInit);
INJECT(0x004474E0, DInputEnumDevices);
INJECT(0x00447510, DInputEnumDevicesCallback);
INJECT(0x00447600, FlaggedStringCreate);
INJECT(0x00447620, GetJoystick);
INJECT(0x00447670, DInputKeyboardCreate);
INJECT(0x00447740, DInputKeyboardRelease);
INJECT(0x00447770, DInputJoystickCreate);
// INJECT(----------, DInputJoystickRelease);
INJECT(0x00447860, WinInStart);
INJECT(0x00447890, WinInFinish);
INJECT(0x004478A0, WinInRunControlPanel);
}
================================================
FILE: specific/init_input.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef INIT_INPUT_H_INCLUDED
#define INIT_INPUT_H_INCLUDED
#include "global/types.h"
#ifdef FEATURE_INPUT_IMPROVED
typedef enum {
JT_NONE,
JT_DIRECTINPUT,
JT_PLAYSTATION,
JT_XINPUT,
} JOYTYPE;
JOYTYPE GetJoystickType();
bool IsJoyVibrationSupported();
bool IsJoyLedColorSupported();
#endif // FEATURE_INPUT_IMPROVED
/*
* Function list
*/
bool __cdecl DInputCreate(); // 0x004472A0
void __cdecl DInputRelease(); // 0x004472D0
void __cdecl WinInReadKeyboard(LPVOID lpInputData); // 0x004472F0
DWORD __cdecl WinInReadJoystick(int *xPos, int *yPos); // 0x00447350
bool __cdecl WinInputInit(); // 0x00447460
bool __cdecl DInputEnumDevices(JOYSTICK_LIST *joystickList); // 0x004474E0
BOOL CALLBACK DInputEnumDevicesCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef); // 0x00447510
void __thiscall FlaggedStringCreate(STRING_FLAGGED *item, DWORD dwSize); // 0x00447600
JOYSTICK_NODE *__cdecl GetJoystick(GUID *lpGuid); // 0x00447620
void __cdecl DInputKeyboardCreate(); // 0x00447670
void __cdecl DInputKeyboardRelease(); // 0x00447740
bool __cdecl DInputJoystickCreate(); // 0x00447770
void __cdecl DInputJoystickRelease(); // NULL function in the original game
void __cdecl WinInStart(); // 0x00447860
void __cdecl WinInFinish(); // 0x00447890
void __cdecl WinInRunControlPanel(HWND hWnd); // 0x004478A0
#endif // INIT_INPUT_H_INCLUDED
================================================
FILE: specific/init_sound.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/init_sound.h"
#include "global/vars.h"
extern void __thiscall FlaggedStringCreate(STRING_FLAGGED *item, DWORD dwSize);
extern void __thiscall FlaggedStringDelete(STRING_FLAGGED *item);
extern bool FlaggedStringCopy(STRING_FLAGGED *dst, STRING_FLAGGED *src);
#ifdef FEATURE_EXTENDED_LIMITS
DWORD SampleFreqs[370];
LPDIRECTSOUNDBUFFER SampleBuffers[370];
#endif // FEATURE_EXTENDED_LIMITS
SOUND_ADAPTER_NODE *__cdecl GetSoundAdapter(GUID *lpGuid) {
SOUND_ADAPTER_NODE *adapter;
if( lpGuid != NULL ) {
for( adapter = SoundAdapterList.head; adapter; adapter = adapter->next ) {
if( !memcmp(&adapter->body.adapterGuid, lpGuid, sizeof(GUID)) )
return adapter;
}
}
return PrimarySoundAdapter;
}
void __cdecl WinSndFreeAllSamples() {
if( !IsSoundEnabled )
return;
for( DWORD i=0; iRelease();
SampleBuffers[i] = NULL;
}
}
}
bool __cdecl WinSndMakeSample(DWORD sampleIdx, LPWAVEFORMATEX format, const LPVOID data, DWORD dataSize) {
LPVOID lpvAudioPtr;
DWORD dwAudioBytes;
DSBUFFERDESC desc;
if( DSound == NULL || !IsSoundEnabled || sampleIdx >= ARRAY_SIZE(SampleBuffers) )
return false;
// NOTE: this check is absent in the original game
if( SampleBuffers[sampleIdx] != NULL ) {
SampleBuffers[sampleIdx]->Release();
SampleBuffers[sampleIdx] = NULL;
}
desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLPAN|DSBCAPS_CTRLFREQUENCY|DSBCAPS_LOCSOFTWARE;
desc.dwBufferBytes = dataSize;
desc.dwReserved = 0;
desc.lpwfxFormat = format;
if FAILED(DSound->CreateSoundBuffer(&desc, &SampleBuffers[sampleIdx], NULL))
return false;
if FAILED(SampleBuffers[sampleIdx]->Lock(0, dataSize, &lpvAudioPtr, &dwAudioBytes, NULL, NULL, 0))
return false;
memcpy(lpvAudioPtr, data, dwAudioBytes);
if FAILED(SampleBuffers[sampleIdx]->Unlock(lpvAudioPtr, dwAudioBytes, NULL, 0))
return false;
SampleFreqs[sampleIdx] = format->nSamplesPerSec;
return true;
}
bool __cdecl WinSndIsChannelPlaying(DWORD channel) {
DWORD status;
if( ChannelBuffers[channel] == NULL || FAILED(ChannelBuffers[channel]->GetStatus(&status)) )
return false;
if( (status & DSBSTATUS_PLAYING) == 0 ) {
ChannelBuffers[channel]->Release();
ChannelBuffers[channel] = NULL;
return false;
}
return true;
}
int __cdecl WinSndPlaySample(DWORD sampleIdx, int volume, DWORD pitch, int pan, DWORD flags) {
LPDIRECTSOUNDBUFFER dsBuffer = NULL;
int channel = WinSndGetFreeChannelIndex();
if( channel < 0 )
return -1;
if( FAILED(DSound->DuplicateSoundBuffer(SampleBuffers[sampleIdx], &dsBuffer)) ||
FAILED(dsBuffer->SetVolume(volume)) ||
FAILED(dsBuffer->SetFrequency(SampleFreqs[sampleIdx] * pitch / PHD_ONE)) ||
FAILED(dsBuffer->SetPan(pan)) ||
FAILED(dsBuffer->SetCurrentPosition(0)) ||
FAILED(dsBuffer->Play(0, 0, flags)) )
{
return -2;
}
ChannelSamples[channel] = sampleIdx;
ChannelBuffers[channel] = dsBuffer;
return channel;
}
int __cdecl WinSndGetFreeChannelIndex() {
for( int i=0; i<32; ++i ) {
if( ChannelBuffers[i] == NULL )
return i;
}
for( int i=0; i<32; ++i ) {
if( !WinSndIsChannelPlaying(i) )
return i;
}
return -1;
}
void __cdecl WinSndAdjustVolumeAndPan(int channel, int volume, int pan) {
if( channel >= 0 && ChannelBuffers[channel] != NULL ) {
ChannelBuffers[channel]->SetVolume(volume);
ChannelBuffers[channel]->SetPan(pan);
}
}
void __cdecl WinSndAdjustPitch(int channel, DWORD pitch) {
if( channel >= 0 && ChannelBuffers[channel] != NULL ) {
ChannelBuffers[channel]->SetFrequency(SampleFreqs[ChannelSamples[channel]] * pitch / PHD_ONE);
}
}
void __cdecl WinSndStopSample(int channel) {
if( channel >= 0 && ChannelBuffers[channel] != NULL ) {
ChannelBuffers[channel]->Stop();
ChannelBuffers[channel]->Release();
ChannelBuffers[channel] = NULL;
}
}
bool __cdecl WinSndInit() {
SOUND_ADAPTER_NODE *node, *nextNode;
for( node = SoundAdapterList.head; node; node = nextNode ) {
nextNode = node->next;
FlaggedStringDelete(&node->body.module);
FlaggedStringDelete(&node->body.description);
delete(node);
}
SoundAdapterList.head = NULL;
SoundAdapterList.tail = NULL;
SoundAdapterList.dwCount = 0;
PrimarySoundAdapter = NULL;
if( !DSoundEnumerate(&SoundAdapterList) )
return false;
for( node = SoundAdapterList.head; node; node = node->next ) {
if( node->body.lpAdapterGuid == NULL ) { // Primary adapter GUID is NULL
PrimarySoundAdapter = node;
break;
}
}
return true;
}
bool __cdecl DSoundEnumerate(SOUND_ADAPTER_LIST *adapterList) {
return SUCCEEDED(DirectSoundEnumerate(DSoundEnumCallback, (LPVOID)adapterList));
}
BOOL CALLBACK DSoundEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext) {
SOUND_ADAPTER_LIST *adapterList = (SOUND_ADAPTER_LIST *)lpContext;
SOUND_ADAPTER_NODE *adapterNode = new SOUND_ADAPTER_NODE;
if( adapterNode == NULL )
return TRUE;
adapterNode->next = NULL;
adapterNode->previous = adapterList->tail;
if( !adapterList->head )
adapterList->head = adapterNode;
if( adapterList->tail )
adapterList->tail->next = adapterNode;
adapterList->tail = adapterNode;
adapterList->dwCount++;
if( lpGuid == NULL ) {
memset(&adapterNode->body.adapterGuid, 0, sizeof(GUID));
adapterNode->body.lpAdapterGuid = NULL;
} else {
adapterNode->body.adapterGuid = *lpGuid;
adapterNode->body.lpAdapterGuid = &adapterNode->body.adapterGuid;
}
FlaggedStringCreate(&adapterNode->body.description, 256);
FlaggedStringCreate(&adapterNode->body.module, 256);
lstrcpy(adapterNode->body.description.lpString, lpcstrDescription);
lstrcpy(adapterNode->body.module.lpString, lpcstrModule);
return TRUE;
}
void __cdecl WinSndStart(HWND hWnd) {
memset(SampleBuffers, 0, sizeof(SampleBuffers));
memset(ChannelBuffers, 0, sizeof(ChannelBuffers));
Camera.isLaraMic = SavedAppSettings.LaraMic;
IsSoundEnabled = false;
if( !SavedAppSettings.SoundEnabled || SavedAppSettings.PreferredSoundAdapter == NULL )
return;
SOUND_ADAPTER *preferred = &SavedAppSettings.PreferredSoundAdapter->body;
CurrentSoundAdapter = *preferred;
FlaggedStringCopy(&CurrentSoundAdapter.description, &preferred->description);
FlaggedStringCopy(&CurrentSoundAdapter.module, &preferred->module);
if( !DSoundCreate(CurrentSoundAdapter.lpAdapterGuid) )
return;
if( hWnd == NULL )
hWnd = HGameWindow;
if FAILED(DSound->SetCooperativeLevel(hWnd, DSSCL_EXCLUSIVE))
throw ERR_CantSetDSCooperativeLevel;
if( DSoundBufferTest() )
IsSoundEnabled = true;
}
bool __cdecl DSoundCreate(GUID *lpGuid) {
#if (DIRECTSOUND_VERSION >= 0x800)
return SUCCEEDED(DirectSoundCreate8(lpGuid, &DSound, NULL));
#else // (DIRECTSOUND_VERSION >= 0x800)
return SUCCEEDED(DirectSoundCreate(lpGuid, &DSound, NULL));
#endif // (DIRECTSOUND_VERSION >= 0x800)
}
bool __cdecl DSoundBufferTest() {
WAVEFORMATEX format;
DSBUFFERDESC desc;
LPDIRECTSOUNDBUFFER dsBuffer;
desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
desc.dwBufferBytes = 0;
desc.dwReserved = 0;
desc.lpwfxFormat = NULL;
if FAILED(DSound->CreateSoundBuffer(&desc, &dsBuffer, NULL))
return false;
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = 2;
format.nSamplesPerSec = 11025;
format.nAvgBytesPerSec = 44100;
format.nBlockAlign = 4;
format.wBitsPerSample = 16;
format.cbSize = 0;
bool result = SUCCEEDED(dsBuffer->SetFormat(&format));
dsBuffer->Release();
return result;
}
void __cdecl WinSndFinish() {
WinSndFreeAllSamples();
if( DSound != NULL ) {
DSound->Release();
DSound = NULL;
}
}
bool __cdecl WinSndIsSoundEnabled() {
return IsSoundEnabled;
}
/*
* Inject function
*/
void Inject_InitSound() {
INJECT(0x00447C70, GetSoundAdapter);
INJECT(0x00447CC0, WinSndFreeAllSamples);
INJECT(0x00447CF0, WinSndMakeSample);
INJECT(0x00447E00, WinSndIsChannelPlaying);
INJECT(0x00447E50, WinSndPlaySample);
INJECT(0x00447F40, WinSndGetFreeChannelIndex);
INJECT(0x00447F80, WinSndAdjustVolumeAndPan);
INJECT(0x00447FB0, WinSndAdjustPitch);
INJECT(0x00447FF0, WinSndStopSample);
INJECT(0x00448060, WinSndInit);
INJECT(0x00448100, DSoundEnumerate);
INJECT(0x00448120, DSoundEnumCallback);
INJECT(0x00448210, WinSndStart);
INJECT(0x00448390, DSoundCreate);
INJECT(0x004483B0, DSoundBufferTest);
INJECT(0x00448480, WinSndFinish);
INJECT(0x004484B0, WinSndIsSoundEnabled);
}
================================================
FILE: specific/init_sound.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef INIT_SOUND_H_INCLUDED
#define INIT_SOUND_H_INCLUDED
#include "global/types.h"
#define VOLUME_PCT(x) (DSBVOLUME_MIN+(DSBVOLUME_MAX-DSBVOLUME_MIN)*(x)/100)
/*
* Function list
*/
SOUND_ADAPTER_NODE *__cdecl GetSoundAdapter(GUID *lpGuid); // 0x00447C70
void __cdecl WinSndFreeAllSamples(); // 0x00447CC0
bool __cdecl WinSndMakeSample(DWORD sampleIdx, LPWAVEFORMATEX format, const LPVOID data, DWORD dataSize); // 0x00447CF0
bool __cdecl WinSndIsChannelPlaying(DWORD channel); // 0x00447E00
int __cdecl WinSndPlaySample(DWORD sampleIdx, int volume, DWORD pitch, int pan, DWORD flags); // 0x00447E50
int __cdecl WinSndGetFreeChannelIndex(); // 0x00447F40
void __cdecl WinSndAdjustVolumeAndPan(int channel, int volume, int pan); // 0x00447F80
void __cdecl WinSndAdjustPitch(int channel, DWORD pitch); // 0x00447FB0
void __cdecl WinSndStopSample(int channel); // 0x00447FF0
bool __cdecl WinSndInit(); // 0x00448060
bool __cdecl DSoundEnumerate(SOUND_ADAPTER_LIST *adapterList); // 0x00448100
BOOL CALLBACK DSoundEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext); // 0x00448120
void __cdecl WinSndStart(HWND hWnd); // 0x00448210
bool __cdecl DSoundCreate(GUID *lpGuid); // 0x00448390
bool __cdecl DSoundBufferTest(); // 0x004483B0
void __cdecl WinSndFinish(); // 0x00448480
bool __cdecl WinSndIsSoundEnabled(); // 0x004484B0
#endif // INIT_SOUND_H_INCLUDED
================================================
FILE: specific/input.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/input.h"
#include "game/health.h"
#include "game/invfunc.h"
#include "game/laramisc.h"
#include "specific/display.h"
#include "specific/init_display.h"
#include "specific/init_input.h"
#include "specific/screenshot.h"
#include "specific/winvid.h"
#include "global/vars.h"
#ifdef FEATURE_INPUT_IMPROVED
bool WalkToSidestep = false;
#endif // FEATURE_INPUT_IMPROVED
// Macros
#define KEY_DOWN(a) ((DIKeys[(a)]&0x80)!=0)
#define TOGGLE(a) {(a)=!(a);}
// NOTE: not presented in the original game
static BOOL JoyKey(KEYMAP keyMap) {
#ifdef FEATURE_HUD_IMPROVED
if( keyMap < 4 ) return FALSE; // ignore direction keys
UINT16 key = Layout[CTRL_Joystick].key[keyMap];
return CHK_ANY(JoyKeys, (1 << key));
#else // FEATURE_HUD_IMPROVED
UINT16 key = Layout[CTRL_Custom].key[keyMap];
return ( key >= 0x100 && CHK_ANY(JoyKeys, (1 << key)) );
#endif // FEATURE_HUD_IMPROVED
}
// NOTE: not presented in the original game
static BOOL KbdKey(KEYMAP keyMap, bool isCustom) {
UINT16 key = Layout[isCustom ? CTRL_Custom : CTRL_Default].key[keyMap];
if( key < 0x100 ) {
if KEY_DOWN(key) {
return TRUE;
}
if( key == DIK_LCONTROL ) return KEY_DOWN(DIK_RCONTROL);
if( key == DIK_RCONTROL ) return KEY_DOWN(DIK_LCONTROL);
if( key == DIK_LSHIFT ) return KEY_DOWN(DIK_RSHIFT);
if( key == DIK_RSHIFT ) return KEY_DOWN(DIK_LSHIFT);
if( key == DIK_LMENU ) return KEY_DOWN(DIK_RMENU);
if( key == DIK_RMENU ) return KEY_DOWN(DIK_LMENU);
}
return FALSE;
}
// NOTE: the original function is splitted into JoyKey() and KbdKey()
BOOL __cdecl Key(KEYMAP keyMap) {
return JoyKey(keyMap) || KbdKey(keyMap, true) || (!ConflictLayout[keyMap] && KbdKey(keyMap, false));
}
bool __cdecl S_UpdateInput() {
// NOTE: some of these isF*KeyPressed are presented in the original code.
// But some has been added by me (marked as + below)
// This prevents from multiple actions by one key press
static bool isScreenShotKeyPressed = false; // +
static bool isF1KeyPressed = false; // +
static bool isF2KeyPressed = false; // +
static bool isF3KeyPressed = false;
static bool isF4KeyPressed = false;
static bool isF7KeyPressed = false;
static bool isF8KeyPressed = false;
static bool isF11KeyPressed = false;
static bool isF12KeyPressed = false; // +
static BYTE mediPackCooldown = 0;
bool isShiftKeyPressed;
DISPLAY_MODE_NODE *mode;
DISPLAY_MODE_LIST *modeList;
DISPLAY_MODE targetMode;
APP_SETTINGS newSettings;
int joyXPos = 0;
int joyYPos = 0;
DWORD input = 0;
WinVidSpinMessageLoop(false);
WinInReadKeyboard(DIKeys);
JoyKeys = WinInReadJoystick(&joyXPos, &joyYPos);
// Joystick Y
if( joyYPos < -8 ) {
input |= IN_FORWARD;
}
else if( joyYPos > 8 ) {
input |= IN_BACK;
}
// Joystick X
if( joyXPos < -8 ) {
input |= IN_LEFT;
}
else if( joyXPos > 8 ) {
input |= IN_RIGHT;
}
// Key maps
if( Key(KM_Forward) ) {
input |= IN_FORWARD;
}
if( Key(KM_Back) ) {
input |= IN_BACK;
}
if( Key(KM_Left) ) {
input |= IN_LEFT;
}
if( Key(KM_Right) ) {
input |= IN_RIGHT;
}
if( Key(KM_StepLeft) ) {
input |= IN_STEPL;
}
if( Key(KM_StepRight) ) {
input |= IN_STEPR;
}
if( Key(KM_Slow) ) {
input |= IN_SLOW;
}
if( Key(KM_Jump) ) {
input |= IN_JUMP;
}
if( Key(KM_Action) ) {
input |= IN_ACTION;
}
if( Key(KM_WeaponDraw) ) {
input |= IN_DRAW;
}
if( Key(KM_Flare ) ) {
input |= IN_FLARE;
}
if( Key(KM_Look) ) {
input |= IN_LOOK;
}
if( Key(KM_Roll) ) {
input |= IN_ROLL;
}
if( Key(KM_Option) && Camera.type != CAM_Cinematic ) {
input |= IN_OPTION;
}
// Key combinations and alternatives
#ifdef FEATURE_HUD_IMPROVED
if( Key(KM_Step) ) {
if( CHK_ANY(input, IN_LEFT) ) {
input &= ~(IN_LEFT|IN_FORWARD|IN_BACK);
input |= IN_STEPL;
} else if( CHK_ANY(input, IN_RIGHT) ) {
input &= ~(IN_RIGHT|IN_FORWARD|IN_BACK);
input |= IN_STEPR;
} else {
input |= IN_SLOW;
}
}
#endif // FEATURE_HUD_IMPROVED
#ifdef FEATURE_INPUT_IMPROVED
if( WalkToSidestep && CHK_ANY(input, IN_SLOW) && !CHK_ANY(input, IN_FORWARD|IN_BACK|IN_STEPL|IN_STEPR) ) {
if( CHK_ANY(input, IN_LEFT) ) {
input &= ~IN_LEFT;
input |= IN_STEPL;
} else if( CHK_ANY(input, IN_RIGHT) ) {
input &= ~IN_RIGHT;
input |= IN_STEPR;
}
}
#endif // FEATURE_INPUT_IMPROVED
if( CHK_ALL(input, IN_FORWARD|IN_BACK) ) {
input |= IN_ROLL;
}
if( KEY_DOWN(DIK_RETURN) || CHK_ANY(input, IN_ACTION) ) {
input |= IN_SELECT;
}
// NOTE: there is no KM_WeaponDraw for Deselect in the original game
if( KEY_DOWN(DIK_ESCAPE) || JoyKey(KM_WeaponDraw) ) {
input |= IN_DESELECT;
}
if( CHK_ALL(input, IN_STEPL|IN_STEPR) ) {
input &= ~(IN_STEPL|IN_STEPR);
}
// If FMV is playing just save input status and exit here
if( IsFmvPlaying )
goto EXIT;
// NOTE: this check is absent in the original game
// it fixes a bug, when the player could interfere with the demo level
if( !IsDemoLevelType ) {
// Weapon requests
if( KEY_DOWN(DIK_1) && Inv_RequestItem(ID_PISTOL_OPTION) ) {
Lara.request_gun_type = LGT_Pistols;
}
else if(KEY_DOWN(DIK_2) && Inv_RequestItem(ID_SHOTGUN_OPTION) ) {
Lara.request_gun_type = LGT_Shotgun;
}
else if( KEY_DOWN(DIK_3) && Inv_RequestItem(ID_MAGNUM_OPTION) ) {
Lara.request_gun_type = LGT_Magnums;
}
else if( KEY_DOWN(DIK_4) && Inv_RequestItem(ID_UZI_OPTION) ) {
Lara.request_gun_type = LGT_Uzis;
}
else if( KEY_DOWN(DIK_5) && Inv_RequestItem(ID_HARPOON_OPTION) ) {
Lara.request_gun_type = LGT_Harpoon;
}
else if( KEY_DOWN(DIK_6) && Inv_RequestItem(ID_M16_OPTION) ) {
Lara.request_gun_type = LGT_M16;
}
else if( KEY_DOWN(DIK_7) && Inv_RequestItem(ID_GRENADE_OPTION) ) {
Lara.request_gun_type = LGT_Grenade;
}
else if( KEY_DOWN(DIK_0) && Inv_RequestItem(ID_FLARES_OPTION) ) {
Lara.request_gun_type = LGT_Flare;
}
// MediPack requests
if( mediPackCooldown > 0 ) {
--mediPackCooldown; // MediPack shortcuts have half second cooldown
} else {
if( KEY_DOWN(DIK_8) && Inv_RequestItem(ID_SMALL_MEDIPACK_OPTION) ) {
UseItem(ID_SMALL_MEDIPACK_OPTION);
mediPackCooldown = 15;
}
else if( KEY_DOWN(DIK_9) && Inv_RequestItem(ID_LARGE_MEDIPACK_OPTION) ) {
UseItem(ID_LARGE_MEDIPACK_OPTION);
mediPackCooldown = 15;
}
}
#ifdef FEATURE_CHEAT
// Cheats
static bool isStuffCheatKeyPressed = false;
if( KEY_DOWN(DIK_I) ) {
if( !isStuffCheatKeyPressed ) {
isStuffCheatKeyPressed = true;
input |= IN_STUFFCHEAT;
}
} else {
isStuffCheatKeyPressed = false;
}
if( KEY_DOWN(DIK_O) ) {
input |= IN_DOZYCHEAT;
}
#endif // FEATURE_CHEAT
}
#ifdef FEATURE_BACKGROUND_IMPROVED
static bool isPauseKeyPressed = false;
#ifdef FEATURE_HUD_IMPROVED
if( Key(KM_Pause) )
#else // FEATURE_HUD_IMPROVED
if( KEY_DOWN(DIK_P) )
#endif // FEATURE_HUD_IMPROVED
{
if( !isPauseKeyPressed ) {
isPauseKeyPressed = true;
input |= IN_PAUSE;
}
} else {
isPauseKeyPressed = false;
}
#endif // FEATURE_BACKGROUND_IMPROVED
// Screenshot
#ifdef FEATURE_SCREENSHOT_IMPROVED
if( KEY_DOWN(DIK_BACK) ) { // BackSpace Key instead of S
#else // !FEATURE_SCREENSHOT_IMPROVED
if( KEY_DOWN(DIK_S) ) {
#endif // FEATURE_SCREENSHOT_IMPROVED
if( !isScreenShotKeyPressed ) {
isScreenShotKeyPressed = true;
#if (DIRECT3D_VERSION >= 0x900)
ScreenShot(NULL);
#else // (DIRECT3D_VERSION >= 0x900)
ScreenShot(PrimaryBufferSurface);
#endif // (DIRECT3D_VERSION >= 0x900)
}
} else {
isScreenShotKeyPressed = false;
}
// Save/Load Game
if( !CHK_ANY(GF_GameFlow.flags, GFF_LoadSaveDisabled) ) {
if( KEY_DOWN(DIK_F5) )
input |= IN_SAVE;
else if( KEY_DOWN(DIK_F6) )
input |= IN_LOAD;
}
// Shift Key check
isShiftKeyPressed = KEY_DOWN(DIK_LSHIFT) || KEY_DOWN(DIK_RSHIFT);
// Graphics option toggles
if( SavedAppSettings.RenderMode == RM_Software ) {
// Software Renderer F7 key
if( KEY_DOWN(DIK_F7) ) {
if( !isF7KeyPressed ) {
isF7KeyPressed = true;
#ifndef FEATURE_NOLEGACY_OPTIONS
if( isShiftKeyPressed ) {
// Triple Buffer (Shift + F7)
if( SavedAppSettings.FullScreen ) {
newSettings = SavedAppSettings;
TOGGLE(newSettings.TripleBuffering);
GameApplySettings(&newSettings);
}
} else {
// Perspective Correction (F7). For SW this means Detail Level: single or double perspective distance
TOGGLE(SavedAppSettings.PerspectiveCorrect);
if( SavedAppSettings.PerspectiveCorrect ) {
DetailLevel = 2;
PerspectiveDistance = SW_DETAIL_HIGH;
} else {
DetailLevel = 1;
PerspectiveDistance = SW_DETAIL_MEDIUM;
}
}
#endif // FEATURE_NOLEGACY_OPTIONS
}
} else {
isF7KeyPressed = false;
}
#ifdef FEATURE_VIDEOFX_IMPROVED
// Software Renderer F11 key
if( KEY_DOWN(DIK_F11) ) {
if( !isF11KeyPressed ) {
isF11KeyPressed = true;
// Lighting Contrast (F11)
newSettings = SavedAppSettings;
newSettings.LightingMode = (newSettings.LightingMode + 1) % 3;
GameApplySettings(&newSettings);
}
} else {
isF11KeyPressed = false;
}
#endif // FEATURE_VIDEOFX_IMPROVED
} else {
// Hardware Renderer F7 key
if( KEY_DOWN(DIK_F7) ) {
if( !isF7KeyPressed ) {
isF7KeyPressed = true;
#ifndef FEATURE_NOLEGACY_OPTIONS
if( isShiftKeyPressed ) {
// Triple Buffer (Shift + F7)
if( SavedAppSettings.FullScreen ) {
newSettings = SavedAppSettings;
TOGGLE(newSettings.TripleBuffering);
GameApplySettings(&newSettings);
}
} else
#endif // FEATURE_NOLEGACY_OPTIONS
{
// ZBuffer (F7)
newSettings = SavedAppSettings;
TOGGLE(newSettings.ZBuffer);
GameApplySettings(&newSettings);
}
}
} else {
isF7KeyPressed = false;
}
// Hardware Renderer F8 key
if( KEY_DOWN(DIK_F8) ) {
if( !isF8KeyPressed ) {
isF8KeyPressed = true;
#ifndef FEATURE_NOLEGACY_OPTIONS
if( isShiftKeyPressed ) {
// Perspective Correction (Shift + F8)
newSettings = SavedAppSettings;
TOGGLE(newSettings.PerspectiveCorrect);
GameApplySettings(&newSettings);
} else
#endif // FEATURE_NOLEGACY_OPTIONS
{
// Bilinear Filtering (F8)
newSettings = SavedAppSettings;
TOGGLE(newSettings.BilinearFiltering);
GameApplySettings(&newSettings);
}
}
} else {
isF8KeyPressed = false;
}
// Hardware Renderer F11 key
if( KEY_DOWN(DIK_F11) ) {
if( !isF11KeyPressed ) {
isF11KeyPressed = true;
#ifdef FEATURE_NOLEGACY_OPTIONS
#if defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
// Lighting Contrast (F11)
newSettings = SavedAppSettings;
newSettings.LightingMode = (newSettings.LightingMode + 1) % 3;
GameApplySettings(&newSettings);
#endif // defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
#else // FEATURE_NOLEGACY_OPTIONS
// Dithering (F11)
newSettings = SavedAppSettings;
TOGGLE(newSettings.Dither);
GameApplySettings(&newSettings);
#endif // FEATURE_NOLEGACY_OPTIONS
}
} else {
isF11KeyPressed = false;
}
}
// Check F12 key if view mode is not locked
if( !IsVidModeLock ) {
if( KEY_DOWN(DIK_F12) ) {
if( !isF12KeyPressed ) {
isF12KeyPressed = true;
newSettings = SavedAppSettings;
if( !isShiftKeyPressed ) {
// FullScreen/Windowed Toggle (F12)
TOGGLE(newSettings.FullScreen);
if( SavedAppSettings.FullScreen ) {
// FullScreen to Windowed
#ifdef FEATURE_NOLEGACY_OPTIONS
CLAMPL(newSettings.WindowWidth, 320);
CLAMPL(newSettings.WindowHeight, 240);
newSettings.WindowWidth = CalculateWindowWidth(newSettings.WindowWidth, newSettings.WindowHeight);
#else // FEATURE_NOLEGACY_OPTIONS
int winWidth = MAX(PhdWinWidth, 320);
int winHeight = MAX(PhdWinHeight, 240);
newSettings.WindowHeight = winHeight;
newSettings.WindowWidth = CalculateWindowWidth(winWidth, winHeight);
newSettings.TripleBuffering = false;
#endif // FEATURE_NOLEGACY_OPTIONS
GameApplySettings(&newSettings);
// Reset inner screen size for windowed mode
GameSizer = 1.0;
ScreenSizer = 1.0;
setup_screen_size();
} else {
// Windowed to FullScreen
if( SavedAppSettings.RenderMode == RM_Hardware )
modeList = &CurrentDisplayAdapter.hwDispModeList;
else
modeList = &CurrentDisplayAdapter.swDispModeList;
if( modeList->dwCount > 0 ) {
targetMode.width = GameVidWidth;
targetMode.height = GameVidHeight;
targetMode.bpp = GameVidBPP;
targetMode.vga = VGA_NoVga;
#ifdef FEATURE_NOLEGACY_OPTIONS
if( SavedAppSettings.VideoMode ) {
targetMode.width = SavedAppSettings.VideoMode->body.width;
targetMode.height = SavedAppSettings.VideoMode->body.height;
}
if( modeList->head ) {
targetMode.bpp = modeList->head->body.bpp;
targetMode.vga = modeList->head->body.vga;
}
#endif // FEATURE_NOLEGACY_OPTIONS
for( mode = modeList->head; mode != NULL; mode = mode->next ) {
if( !CompareVideoModes(&mode->body, &targetMode) )
break;
}
if( mode == NULL ) {
mode = modeList->tail;
}
newSettings.VideoMode = mode;
GameApplySettings(&newSettings);
}
}
}
// Check Shift+F12 key if Inventory is not active now
else if( !IsInventoryActive ) {
// Renderer Toggle (Shift + F12)
if( SavedAppSettings.RenderMode == RM_Software ) {
// Software to Hardware
newSettings.RenderMode = RM_Hardware;
modeList = &CurrentDisplayAdapter.hwDispModeList;
targetMode.bpp = 16;
} else {
// Hardware to Software
newSettings.RenderMode = RM_Software;
modeList = &CurrentDisplayAdapter.swDispModeList;
targetMode.bpp = 8;
}
if( modeList->dwCount > 0 ) {
targetMode.width = GameVidWidth;
targetMode.height = GameVidHeight;
targetMode.vga = VGA_NoVga;
#ifdef FEATURE_NOLEGACY_OPTIONS
if( SavedAppSettings.VideoMode ) {
targetMode.width = SavedAppSettings.VideoMode->body.width;
targetMode.height = SavedAppSettings.VideoMode->body.height;
}
if( modeList->head ) {
targetMode.bpp = modeList->head->body.bpp;
targetMode.vga = modeList->head->body.vga;
}
#endif // FEATURE_NOLEGACY_OPTIONS
for( mode = modeList->head; mode != NULL; mode = mode->next ) {
if( !CompareVideoModes(&mode->body, &targetMode) )
break;
}
if( mode == NULL ) {
mode = modeList->tail;
}
newSettings.VideoMode = mode;
#if (DIRECT3D_VERSION < 0x900)
newSettings.FullScreen = true;
#endif // (DIRECT3D_VERSION < 0x900)
GameApplySettings(&newSettings);
}
}
}
} else {
isF12KeyPressed = false;
}
}
#ifdef FEATURE_NOLEGACY_OPTIONS
if( SavedAppSettings.RenderMode == RM_Software ) {
char msg[32] = {0};
const char *levels[3] = {
GF_GameStringTable[GSI_Detail_Low],
GF_GameStringTable[GSI_Detail_Medium],
GF_GameStringTable[GSI_Detail_High],
};
// Decrease Software Renderer Detail Level
if( KEY_DOWN(DIK_F3) ) {
if( !isF3KeyPressed && DetailLevel > 0 ) {
isF3KeyPressed = true;
switch( --DetailLevel ) {
case 0: PerspectiveDistance = SW_DETAIL_LOW; break;
case 1: PerspectiveDistance = SW_DETAIL_MEDIUM; break;
case 2: PerspectiveDistance = SW_DETAIL_HIGH; break;
}
snprintf(msg, sizeof(msg), "Detail Level: %s", levels[DetailLevel]);
}
} else {
isF3KeyPressed = false;
}
// Increase Software Renderer Detail Level
if( KEY_DOWN(DIK_F4) ) {
if( !isF4KeyPressed && DetailLevel < 2 ) {
isF4KeyPressed = true;
switch( ++DetailLevel ) {
case 0: PerspectiveDistance = SW_DETAIL_LOW; break;
case 1: PerspectiveDistance = SW_DETAIL_MEDIUM; break;
case 2: PerspectiveDistance = SW_DETAIL_HIGH; break;
}
snprintf(msg, sizeof(msg), "Detail Level: %s", levels[DetailLevel]);
}
} else {
isF4KeyPressed = false;
}
if( *msg ) DisplayModeInfo(msg);
}
#endif // FEATURE_NOLEGACY_OPTIONS
// Check if we cannot change full screen video parameters here
if( IsVidSizeLock ||
Camera.type == CAM_Cinematic ||
!SavedAppSettings.FullScreen ||
CHK_ANY(GF_GameFlow.flags, GFF_ScreenSizingDisabled) )
{
goto EXIT;
}
// Decrease resolution/depth (F1)
if( KEY_DOWN(DIK_F1) ) {
if( !isF1KeyPressed ) {
isF1KeyPressed = true;
mode = SavedAppSettings.VideoMode;
// Get previous videomode (enough for Software Renderer)
if( mode != NULL ) {
mode = mode->previous;
}
// Additional checks for Hardware Renderer
if( SavedAppSettings.RenderMode == RM_Hardware ) {
for( ; mode != NULL; mode = mode->previous ) {
#ifndef FEATURE_NOLEGACY_OPTIONS
// Decrease depth (Shift + F1)
if( isShiftKeyPressed ) {
if( mode->body.width == SavedAppSettings.VideoMode->body.width &&
mode->body.height == SavedAppSettings.VideoMode->body.height &&
mode->body.vga == SavedAppSettings.VideoMode->body.vga &&
mode->body.bpp < SavedAppSettings.VideoMode->body.bpp )
{
break;
}
} else
#endif // FEATURE_NOLEGACY_OPTIONS
// Decrease resolution (F1)
{
if( mode->body.vga == SavedAppSettings.VideoMode->body.vga &&
mode->body.bpp == SavedAppSettings.VideoMode->body.bpp )
{
break;
}
}
}
}
if( mode != NULL ) {
newSettings = SavedAppSettings;
newSettings.VideoMode = mode;
GameApplySettings(&newSettings);
}
}
} else {
isF1KeyPressed = false;
}
// Increase resolution/depth (F2)
if( KEY_DOWN(DIK_F2) ) {
if( !isF2KeyPressed ) {
isF2KeyPressed = true;
mode = SavedAppSettings.VideoMode;
// Get next videomode (enough for Software Renderer)
if( mode != NULL ) {
mode = mode->next;
}
// Additional checks for Hardware Renderer
if( SavedAppSettings.RenderMode == RM_Hardware ) {
for( ; mode != NULL; mode = mode->next ) {
#ifndef FEATURE_NOLEGACY_OPTIONS
// Increase depth (Shift + F2)
if( isShiftKeyPressed ) {
if( mode->body.width == SavedAppSettings.VideoMode->body.width &&
mode->body.height == SavedAppSettings.VideoMode->body.height &&
mode->body.vga == SavedAppSettings.VideoMode->body.vga &&
mode->body.bpp > SavedAppSettings.VideoMode->body.bpp )
{
break;
}
} else
#endif // FEATURE_NOLEGACY_OPTIONS
// Increase resolution (F2)
{
if( mode->body.vga == SavedAppSettings.VideoMode->body.vga &&
mode->body.bpp == SavedAppSettings.VideoMode->body.bpp )
{
break;
}
}
}
}
if( mode != NULL ) {
newSettings = SavedAppSettings;
newSettings.VideoMode = mode;
GameApplySettings(&newSettings);
}
}
}else {
isF2KeyPressed = false;
}
#ifndef FEATURE_NOLEGACY_OPTIONS
// Decrease inner screen size (F3)
if( KEY_DOWN(DIK_F3) ) {
if( !isF3KeyPressed ) {
isF3KeyPressed = true;
DecreaseScreenSize();
}
} else {
isF3KeyPressed = false;
}
// Increase inner screen size (F4)
if( KEY_DOWN(DIK_F4) ) {
if( !isF4KeyPressed ) {
isF4KeyPressed = true;
IncreaseScreenSize();
}
} else {
isF4KeyPressed = false;
}
#endif // !FEATURE_NOLEGACY_OPTIONS
EXIT :
InputStatus = input;
return IsGameToExit;
}
/*
* Inject function
*/
void Inject_Input() {
INJECT(0x0044D9B0, Key);
INJECT(0x0044DAD0, S_UpdateInput);
}
================================================
FILE: specific/input.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef INPUT_H_INCLUDED
#define INPUT_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
BOOL __cdecl Key(KEYMAP keyMap); // 0x0044D9B0
bool __cdecl S_UpdateInput(); // 0x0044DAD0
#endif // INPUT_H_INCLUDED
================================================
FILE: specific/option.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/option.h"
#include "game/invtext.h"
#include "game/sound.h"
#include "game/text.h"
#include "specific/game.h"
#include "specific/init_input.h"
#include "specific/output.h"
#include "specific/sndpc.h"
#include "global/vars.h"
#ifdef FEATURE_HUD_IMPROVED
#include "modding/psx_bar.h"
extern DWORD InvTextBoxMode;
bool JoystickHintsEnabled = true;
CONTROL_LAYOUT Layout[3] = {
{ // Joystick Layout
0,0,0,0, // directions are not mappable, so they are ignored
10, // Step Left
11, // Step Right
7, // Step
5, // Walk
3, // Jump
2, // Action
1, // Roll
0, // Draw Weapon
6, // Flare
4, // Look
8, // Inventory
9, // Pause
},
{ // User Keyboard Layout
DIK_NUMPAD8, // Run
DIK_NUMPAD2, // Back
DIK_NUMPAD4, // Left
DIK_NUMPAD6, // Right
DIK_NUMPAD7, // Step Left
DIK_NUMPAD9, // Step Right
DIK_DIVIDE, // Step
DIK_NUMPAD1, // Walk
DIK_ADD, // Jump
DIK_NUMPADENTER, // Action
DIK_NUMPAD5, // Roll
DIK_NUMPAD3, // Draw Weapon
DIK_SUBTRACT, // Flare
DIK_NUMPAD0, // Look
DIK_DECIMAL, // Inventory
DIK_MULTIPLY, // Pause
},
{ // Default Keyboard Layout
DIK_UP, // Run
DIK_DOWN, // Back
DIK_LEFT, // Left
DIK_RIGHT, // Right
DIK_DELETE, // Step Left
DIK_NEXT, // Step Right
DIK_PERIOD, // Step
DIK_RSHIFT, // Walk
DIK_RMENU, // Jump
DIK_RCONTROL, // Action
DIK_END, // Roll
DIK_SPACE, // Draw Weapon
DIK_SLASH, // Flare
DIK_NUMPAD0, // Look
DIK_ESCAPE, // Inventory
DIK_P, // Pause
},
};
INVENTORY_ITEM *InvOptionList[5] = {
&InvPassportOption,
&InvSoundOption,
&InvDetailOption,
&InvControlOption,
&InvPhotoOption,
};
__int16 InvOptionObjectsCount = ARRAY_SIZE(InvOptionList);
bool ConflictLayout[ARRAY_SIZE(Layout->key)];
TEXT_STR_INFO *CtrlTextA[ARRAY_SIZE(Layout->key)];
TEXT_STR_INFO *CtrlTextB[ARRAY_SIZE(Layout->key)];
TEXT_STR_INFO *CtrlTextC[ARRAY_SIZE(Layout->key)];
static DWORD LayoutPage = CTRL_Joystick;
#else // FEATURE_HUD_IMPROVED
static DWORD LayoutPage = CTRL_Default;
#endif // FEATURE_HUD_IMPROVED
/*
* Passport option box parameters
*/
#define PASSPORT_LINE_COUNT (10)
// Y coordinates relative to the bottom of the screen
#ifdef FEATURE_HUD_IMPROVED
#define PASSPORT_Y_BOX (-44)
#else // FEATURE_HUD_IMPROVED
#define PASSPORT_Y_BOX (-32)
#endif
#define PASSPORT_Y_TITLE (-16)
/*
* Detail option box parameters
*/
#define DETAIL_WIDTH_L (160)
#define DETAIL_WIDTH_M (DETAIL_WIDTH_L - 4)
#define DETAIL_WIDTH_S (DETAIL_WIDTH_L - 12)
#define DETAIL_LN_HEIGHT (25)
#define DETAIL_HEIGHT (DETAIL_LN_HEIGHT * 3 + 32)
// Y coordinates relative to the center of the screen
#define DETAIL_Y_BOX (-32)
#define DETAIL_Y_TITLE (DETAIL_Y_BOX + 2)
#define DETAIL_Y_LINE1 (DETAIL_LN_HEIGHT * 0)
#define DETAIL_Y_LINE2 (DETAIL_LN_HEIGHT * 1)
#define DETAIL_Y_LINE3 (DETAIL_LN_HEIGHT * 2)
#define DETAIL_NEARZ (8)
#define DETAIL_FARZ (16)
/*
* Sound option box parameters
*/
#define SOUND_WIDTH_L (140)
#define SOUND_WIDTH_M (SOUND_WIDTH_L - 4)
#define SOUND_WIDTH_S (SOUND_WIDTH_L - 12)
#define SOUND_LN_HEIGHT (25)
#define SOUND_HEIGHT (SOUND_LN_HEIGHT * 2 + 32)
// Y coordinates relative to the center of the screen
#define SOUND_Y_BOX (-32)
#define SOUND_Y_TITLE (SOUND_Y_BOX + 2)
#define SOUND_Y_LINE1 (SOUND_LN_HEIGHT * 0)
#define SOUND_Y_LINE2 (SOUND_LN_HEIGHT * 1)
#define SOUND_NEARZ (8)
#define SOUND_FARZ (48)
/*
* Control option box parameters
*/
#define CONTROL_LINE_COUNT (ARRAY_SIZE(Layout->key)/2)
#define CONTROL_LN_HEIGHT (15)
#ifdef FEATURE_HUD_IMPROVED
#define CONTROL_WIDTH_LOW (300)
#define CONTROL_HEIGHT_LOW (CONTROL_LN_HEIGHT * CONTROL_LINE_COUNT + 26)
#define CONTROL_WIDTH_HIGH (360)
#define CONTROL_HEIGHT_HIGH CONTROL_HEIGHT_LOW
#define CONTROL_COLUMN_C (15)
#define CONTROL_COLUMN_B2 (30)
#define CONTROL_COLUMN_B1 (40)
#define CONTROL_COLUMN_A (60)
// Y coordinates relative to the center of the screen
#define CONTROL_Y_BOX (-70)
#define CONTROL_Y_TITLE (CONTROL_Y_BOX + 4)
#define CONTROL_Y_LINE1 (CONTROL_LN_HEIGHT * 0 + CONTROL_Y_BOX + 24)
#define CONTROL_Y_LINE2 (CONTROL_LN_HEIGHT * 1 + CONTROL_Y_BOX + 24)
#define CONTROL_Y_LINE3 (CONTROL_LN_HEIGHT * 2 + CONTROL_Y_BOX + 24)
#define CONTROL_Y_LINE4 (CONTROL_LN_HEIGHT * 3 + CONTROL_Y_BOX + 24)
#define CONTROL_Y_LINE5 (CONTROL_LN_HEIGHT * 4 + CONTROL_Y_BOX + 24)
#define CONTROL_Y_LINE6 (CONTROL_LN_HEIGHT * 5 + CONTROL_Y_BOX + 24)
#define CONTROL_Y_LINE7 (CONTROL_LN_HEIGHT * 6 + CONTROL_Y_BOX + 24)
#define CONTROL_Y_LINE8 (CONTROL_LN_HEIGHT * 7 + CONTROL_Y_BOX + 24)
#else // FEATURE_HUD_IMPROVED
#define CONTROL_WIDTH_LOW (300)
#define CONTROL_HEIGHT_LOW (CONTROL_LN_HEIGHT * CONTROL_LINE_COUNT + 35)
#define CONTROL_WIDTH_HIGH (420)
#define CONTROL_HEIGHT_HIGH (CONTROL_LN_HEIGHT * CONTROL_LINE_COUNT + 45)
#define CONTROL_COLUMN_B (10)
#define CONTROL_COLUMN_A (80)
// Y coordinates relative to the center of the screen
#define CONTROL_Y_BOX (-55)
#define CONTROL_Y_TITLE (CONTROL_Y_BOX + 5)
#define CONTROL_Y_LINE1 (CONTROL_LN_HEIGHT * 0 + CONTROL_Y_BOX + 30)
#define CONTROL_Y_LINE2 (CONTROL_LN_HEIGHT * 1 + CONTROL_Y_BOX + 30)
#define CONTROL_Y_LINE3 (CONTROL_LN_HEIGHT * 2 + CONTROL_Y_BOX + 30)
#define CONTROL_Y_LINE4 (CONTROL_LN_HEIGHT * 3 + CONTROL_Y_BOX + 30)
#define CONTROL_Y_LINE5 (CONTROL_LN_HEIGHT * 4 + CONTROL_Y_BOX + 30)
#define CONTROL_Y_LINE6 (CONTROL_LN_HEIGHT * 5 + CONTROL_Y_BOX + 30)
#define CONTROL_Y_LINE7 (CONTROL_LN_HEIGHT * 6 + CONTROL_Y_BOX + 30)
#endif // FEATURE_HUD_IMPROVED
#define CONTROL_NEARZ (16)
#define CONTROL_FARZ (48)
extern GOURAUD_FILL ReqBgndGour1;
extern GOURAUD_OUTLINE ReqBgndGour2;
extern GOURAUD_FILL ReqMainGour1;
extern GOURAUD_OUTLINE ReqMainGour2;
extern GOURAUD_FILL ReqSelGour1;
extern GOURAUD_OUTLINE ReqSelGour2;
/*
* Control key names
*/
#ifdef FEATURE_HUD_IMPROVED
static LPCSTR ControlKeysText[0x110] = {
NULL, K("esc"), K("1"), K("2"), K("3"), K("4"), K("5"), K("6"),
K("7"), K("8"), K("9"), K("0"), K("-"), K("="), K("backspace"), K("tab"),
K("q"), K("w"), K("e"), K("r"), K("t"), K("y"), K("u"), K("i"),
K("o"), K("p"), K("["), K("]"), K("return"), K("ctrl"), K("a"), K("s"),
K("d"), K("f"), K("g"), K("h"), K("j"), K("k"), K("l"), K(";"),
K("'"), K("`"), K("shift"), K("\\"), K("z"), K("x"), K("c"), K("v"),
K("b"), K("n"), K("m"), K(","), K("."), K("/"), K("shift"), K("pad*"),
K("alt"), K("space"), K("capslock"), NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, K("numlock"), NULL, K("pad7"),
K("pad8"), K("pad9"), K("pad-"), K("pad4"), K("pad5"), K("pad6"), K("pad+"), K("pad1"),
K("pad2"), K("pad3"), K("pad0"), K("pad."), NULL, NULL, K("<"), NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, K("enter"), K("ctrl"), NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, K("shift"), NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, K("pad/"), NULL, NULL,
K("alt"), NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, K("home"),
K("up"), K("pageup"), NULL, K("left"), NULL, K("right"), NULL, K("end"),
K("down"), K("pagedown"), K("insert"), K("delete"), NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
K("joy1"), K("joy2"), K("joy3"), K("joy4"), K("joy5"), K("joy6"), K("joy7"), K("joy8"),
K("joy9"), K("joy10"), K("joy11"), K("joy12"), K("joy13"), K("joy14"), K("joy15"), K("joy16"),
};
static TEXT_STR_INFO *SelectHintText, *ContinueHintText, *DeselectHintText;
typedef enum {
HINT_HIDDEN,
HINT_JOYSTICK,
HINT_KEYBOARD,
} HINT_MODE;
static const char *GetHintText(HINT_MODE mode, KEYMAP joyKeyMap, DWORD kbdKeyCode, const char *message) {
static char text[64] = {0};
const char *key = NULL;
if( mode == HINT_JOYSTICK && Layout[CTRL_Joystick].key[joyKeyMap] < 0x10 ) {
key = ControlKeysText[0x100 + Layout[CTRL_Joystick].key[joyKeyMap]];
} else {
key = ControlKeysText[kbdKeyCode];
}
snprintf(text, sizeof(text), "%s %s", key, message);
return text;
}
static void UpdateJoystickHintText(HINT_MODE selectMode, HINT_MODE continueMode, HINT_MODE deselectMode) {
if( SelectHintText != NULL ) {
if( selectMode != HINT_HIDDEN ) {
T_ChangeText(SelectHintText, GetHintText(selectMode, KM_Action, DIK_RETURN, "Select"));
}
T_HideText(SelectHintText, selectMode == HINT_HIDDEN);
}
if( ContinueHintText != NULL ) {
if( continueMode != HINT_HIDDEN ) {
T_ChangeText(ContinueHintText, GetHintText(continueMode, KM_Action, DIK_RETURN, "Continue"));
}
T_HideText(ContinueHintText, continueMode == HINT_HIDDEN);
}
if( DeselectHintText != NULL ) {
if( deselectMode != HINT_HIDDEN ) {
T_ChangeText(DeselectHintText, GetHintText(deselectMode, KM_WeaponDraw, DIK_ESCAPE, "Go Back"));
}
T_HideText(DeselectHintText, deselectMode == HINT_HIDDEN);
}
}
void RemoveJoystickHintText(bool isSelect, bool isContinue, bool isDeselect) {
if( isSelect ) {
T_RemovePrint(SelectHintText);
SelectHintText = NULL;
}
if( isContinue ) {
T_RemovePrint(ContinueHintText);
ContinueHintText = NULL;
}
if( isDeselect ) {
T_RemovePrint(DeselectHintText);
DeselectHintText = NULL;
}
}
void DisplayJoystickHintText(bool isSelect, bool isContinue, bool isDeselect) {
if( !JoystickHintsEnabled ) return;
#ifdef FEATURE_INPUT_IMPROVED
if( GetJoystickType() == JT_NONE ) return;
#else // FEATURE_INPUT_IMPROVED
if( !SavedAppSettings.JoystickEnabled || !SavedAppSettings.PreferredJoystick ) return;
#endif // FEATURE_INPUT_IMPROVED
bool isRealignX = false;
static int renderWidth = 0;
if( renderWidth != GetRenderWidthDownscaled() ) {
isRealignX = true;
renderWidth = GetRenderWidthDownscaled();
}
int x = (renderWidth > 325) ? (renderWidth - 320) / 2 : 2;
if( isSelect && SelectHintText == NULL ) {
SelectHintText = T_Print(x, -40, 0, GetHintText(HINT_JOYSTICK, KM_Action, DIK_RETURN, "Select"));
T_BottomAlign(SelectHintText, 1);
} else if( isRealignX && SelectHintText != NULL ) {
SelectHintText->xPos = x;
}
if( isContinue && ContinueHintText == NULL ) {
ContinueHintText = T_Print(0, -40, 0, GetHintText(HINT_JOYSTICK, KM_Action, DIK_RETURN, "Continue"));
T_BottomAlign(ContinueHintText, 1);
T_CentreH(ContinueHintText, 1);
}
if( isDeselect && DeselectHintText == NULL ) {
DeselectHintText = T_Print(-x, -40, 0, GetHintText(HINT_JOYSTICK, KM_WeaponDraw, DIK_ESCAPE, "Go Back"));
T_BottomAlign(DeselectHintText, 1);
T_RightAlign(DeselectHintText, 1);
} else if( isRealignX && DeselectHintText != NULL ) {
DeselectHintText->xPos = -x;
}
}
static void DrawVolumeBar(int x, int y, int percent, int alpha) {
// draw at least one percent
CLAMP(percent, 1, 100);
// coordinates are relative to the center of the screen
int width = GetRenderScale(100);
int height = GetRenderScale(5);
int pixel = GetRenderScale(1);
int x0 = PhdWinMinX + GetRenderScale(x) + (GetRenderWidth() - width) / 2;
int x1 = x0 + width;
int y0 = PhdWinMinY + GetRenderScale(y) + (GetRenderHeight() - height) / 2;
int y1 = y0 + height;
int bar = width * percent / 100;
if( SavedAppSettings.ZBuffer ) {
PSX_DrawAirBar(x0, y0, x1, y1, bar, pixel, alpha);
} else {
PSX_InsertAirBar(x0, y0, x1, y1, bar, pixel, alpha);
}
}
void DisplayVolumeBars(bool isSmooth) {
static int soundVolumePos = 0;
static int musicVolumePos = 0;
if( isSmooth ) {
if( musicVolumePos < MusicVolume*10 ) {
musicVolumePos += 2;
} else if( musicVolumePos > MusicVolume*10 ) {
musicVolumePos -= 2;
}
if( soundVolumePos < SoundVolume*10 ) {
soundVolumePos += 2;
} else if( soundVolumePos > SoundVolume*10 ) {
soundVolumePos -= 2;
}
} else {
musicVolumePos = MusicVolume*10;
soundVolumePos = SoundVolume*10;
}
DrawVolumeBar(10, -5, musicVolumePos, SoundOptionLine ? 100 : 255);
DrawVolumeBar(10, 20, soundVolumePos, SoundOptionLine ? 255 : 100);
if( SoundOptionLine ) {
PlaySoundEffect(113, NULL, SFX_ALWAYS); // ticking clock sound
}
}
static const char *GetEmptyOptName(void) {
return "";
}
static const char *GetEmptyOptState(void) {
return "";
}
static const char *ToggleEmptyOptState(void) {
return GetEmptyOptState();
}
static const char *GetShowHintsOptName(void) {
return "Show Hints";
}
static const char *GetShowHintsOptState(void) {
return JoystickHintsEnabled ? GF_SpecificStringTable[SSI_On] : GF_SpecificStringTable[SSI_Off];
}
static const char *ToggleShowHintsOptState(void) {
JoystickHintsEnabled = !JoystickHintsEnabled;
if( JoystickHintsEnabled ) {
DisplayJoystickHintText(true, false, true);
} else {
RemoveJoystickHintText(true, false, true);
}
return GetShowHintsOptState();
}
#ifdef FEATURE_INPUT_IMPROVED
extern bool JoystickVibrationEnabled;
extern bool JoystickLedColorEnabled;
static const char *GetVibrationOptName(void) {
return "Vibration";
}
static const char *GetVibrationOptState(void) {
if( !IsJoyVibrationSupported() ) return GF_GameStringTable[GSI_String_NA];
return JoystickVibrationEnabled ? GF_SpecificStringTable[SSI_On] : GF_SpecificStringTable[SSI_Off];
}
static const char *ToggleVibrationOptState(void) {
if( IsJoyVibrationSupported() ) {
JoystickVibrationEnabled = !JoystickVibrationEnabled;
}
return GetVibrationOptState();
}
static const char *GetLightbarOptName(void) {
return IsJoyLedColorSupported() ? "Light Bar" : "";
}
static const char *GetLightbarOptState(void) {
if( !IsJoyLedColorSupported() ) return "";
return JoystickLedColorEnabled ? GF_SpecificStringTable[SSI_On] : GF_SpecificStringTable[SSI_Off];
}
static const char *ToggleLightbarOptState(void) {
if( IsJoyLedColorSupported() ) {
JoystickLedColorEnabled = !JoystickLedColorEnabled;
}
return GetLightbarOptState();
}
#endif // FEATURE_INPUT_IMPROVED
typedef struct {
const char *(*getName)(void);
const char *(*getState)(void);
const char *(*toggle)(void);
} JOYOPT;
static JOYOPT JoystickOpts[4] = {
{GetShowHintsOptName, GetShowHintsOptState, ToggleShowHintsOptState},
#ifdef FEATURE_INPUT_IMPROVED
{GetVibrationOptName, GetVibrationOptState, ToggleVibrationOptState},
{GetLightbarOptName, GetLightbarOptState, ToggleLightbarOptState},
#else // FEATURE_INPUT_IMPROVED
{GetEmptyOptName, GetEmptyOptState, ToggleEmptyOptState},
{GetEmptyOptName, GetEmptyOptState, ToggleEmptyOptState},
#endif // FEATURE_INPUT_IMPROVED
{GetEmptyOptName, GetEmptyOptState, ToggleEmptyOptState},
};
#else // FEATURE_HUD_IMPROVED
static LPCSTR ControlKeysText[0x110] = {
NULL, "ESC", "1", "2", "3", "4", "5", "6",
"7", "8", "9", "0", "-", "+", "BKSP", "TAB",
"Q", "W", "E", "R", "T", "Y", "U", "I",
"O", "P", "<", ">", "RET", "CTRL", "A", "S",
"D", "F", "G", "H", "J", "K", "L", ";",
"'", "`", "SHIFT", "#", "Z", "X", "C", "V",
"B", "N", "M", ",", ".", "/", "SHIFT", "PADx",
"ALT", "SPACE", "CAPS", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, "NMLK", NULL, "PAD7",
"PAD8", "PAD9", "PAD-", "PAD4", "PAD5", "PAD6", "PAD+", "PAD1",
"PAD2", "PAD3", "PAD0", "PAD.", NULL, NULL, "\\", NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, "ENTER", "CTRL", NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, "SHIFT", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, "PAD/", NULL, NULL,
"ALT", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, "HOME",
"UP", "PGUP", NULL, "LEFT", NULL, "RIGHT", NULL, "END",
"DOWN", "PGDN", "INS", "DEL", NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
"JOY1", "JOY2", "JOY3", "JOY4", "JOY5", "JOY6", "JOY7", "JOY8",
"JOY9", "JOY10", "JOY11", "JOY12", "JOY13", "JOY14", "JOY15", "JOY16",
};
#endif // FEATURE_HUD_IMPROVED
// NOTE: not presented in the original game. SetPCRequesterSize() used directly instead
void SetPassportRequesterSize(REQUEST_INFO *req) {
#ifdef FEATURE_HUD_IMPROVED
extern DWORD SavegameSlots;
double scale = (double)GetRenderHeight() / (double)GetRenderScale(480);
DWORD adjust = ( scale > 1.0 ) ? 5 : 0;
DWORD lines = (PASSPORT_LINE_COUNT + adjust) * scale - adjust;
CLAMP(lines, 5, SavegameSlots);
SetPCRequesterSize(req, lines, PASSPORT_Y_BOX);
#else // !FEATURE_HUD_IMPROVED
SetPCRequesterSize(req, PASSPORT_LINE_COUNT, PASSPORT_Y_BOX);
#endif // FEATURE_HUD_IMPROVED
}
void __cdecl do_inventory_options(INVENTORY_ITEM *item) {
switch( item->objectID ) {
// passport
case ID_PASSPORT_OPTION:
do_passport_option(item);
break;
// gamma
case ID_GAMMA_OPTION:
do_gamma_option(item);
break;
// detail
case ID_DETAIL_OPTION:
do_detail_option(item);
break;
// sound
case ID_SOUND_OPTION:
do_sound_option(item);
break;
// control
case ID_CONTROL_OPTION:
do_control_option(item);
break;
// statistics
case ID_COMPASS_OPTION:
do_compass_option(item);
break;
// weapons
case ID_PISTOL_OPTION:
case ID_SHOTGUN_OPTION:
case ID_MAGNUM_OPTION:
case ID_UZI_OPTION:
case ID_HARPOON_OPTION:
case ID_M16_OPTION:
case ID_GRENADE_OPTION:
// medipacks
case ID_SMALL_MEDIPACK_OPTION:
case ID_LARGE_MEDIPACK_OPTION:
// puzzles
case ID_PUZZLE_OPTION1:
case ID_PUZZLE_OPTION2:
case ID_PUZZLE_OPTION3:
case ID_PUZZLE_OPTION4:
// keys
case ID_KEY_OPTION1:
case ID_KEY_OPTION2:
case ID_KEY_OPTION3:
case ID_KEY_OPTION4:
// pickups
case ID_PICKUP_OPTION1:
case ID_PICKUP_OPTION2:
InputDB |= IN_SELECT;
break;
// ammo
case ID_PISTOL_AMMO_OPTION:
case ID_SHOTGUN_AMMO_OPTION:
case ID_MAGNUM_AMMO_OPTION:
case ID_UZI_AMMO_OPTION:
case ID_HARPOON_AMMO_OPTION:
case ID_M16_AMMO_OPTION:
case ID_GRENADE_AMMO_OPTION:
break;
// other
default:
if( CHK_ANY(InputDB, IN_SELECT|IN_DESELECT) ) {
item->goalFrame = 0;
item->animDirection = -1;
}
break;
}
}
#ifdef FEATURE_HUD_IMPROVED
static void SetPassportTextInfo(GAME_STRING_ID id, bool left, bool right) {
if( SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode ) {
char text[64] = {0};
snprintf(text, sizeof(text), "%c %s %c ", left?'\x11':' ', GF_GameStringTable[id], right?'\x12':' ');
PassportTextInfo = T_Print(0, PASSPORT_Y_TITLE, 0, text);
} else {
PassportTextInfo = T_Print(0, PASSPORT_Y_TITLE, 0, GF_GameStringTable[id]);
}
}
#endif // FEATURE_HUD_IMPROVED
void __cdecl do_passport_option(INVENTORY_ITEM *item) {
static int passportMode = 0;
int frame, page, select;
REQUEST_INFO *requester;
T_RemovePrint(InvItemText[0]);
InvItemText[0] = NULL;
frame = item->goalFrame - item->openFrame;
page = ( (frame % 5) == 0 ) ? (frame / 5) : -1;
#ifdef FEATURE_HUD_IMPROVED
bool left = page > 0;
bool right = page < 2;
if( InventoryMode == INV_DeathMode ) {
InputDB &= ~IN_DESELECT;
}
#endif // FEATURE_HUD_IMPROVED
if( InventoryMode == INV_LoadMode ||
InventoryMode == INV_SaveMode ||
CHK_ANY(GF_GameFlow.flags, GFF_LoadSaveDisabled) )
{
InputDB &= ~(IN_LEFT|IN_RIGHT);
#ifdef FEATURE_HUD_IMPROVED
left = false;
right = false;
#endif // FEATURE_HUD_IMPROVED
}
InventoryExtraData[0] = page; // NOTE: moved here from the end
switch( page ) {
case 0 : // load game
if( CHK_ANY(GF_GameFlow.flags, GFF_LoadSaveDisabled) ) {
InputDB = IN_RIGHT;
}
else if( passportMode == 1 ) {
SetPassportRequesterSize(&LoadGameRequester);
select = Display_Requester(&LoadGameRequester, TRUE, TRUE);
if( select == 0 ) {
if( CHK_ANY(InputDB, IN_RIGHT) ) {
Remove_Requester(&LoadGameRequester);
passportMode = 0;
} else {
InputStatus = 0;
InputDB = 0;
}
} else {
if( select > 0 ) {
InventoryExtraData[1] = select - 1;
}
passportMode = 0;
}
}
else if( passportMode == 0 ) {
if( SavedGamesCount == 0 || InventoryMode == INV_SaveMode ) {
InputDB = IN_RIGHT;
} else {
if( PassportTextInfo == NULL ) {
#ifdef FEATURE_HUD_IMPROVED
SetPassportTextInfo(GSI_Passport_LoadGame, left, right);
#else // FEATURE_HUD_IMPROVED
PassportTextInfo = T_Print(0, PASSPORT_Y_TITLE, 0, GF_GameStringTable[GSI_Passport_LoadGame]);
#endif // FEATURE_HUD_IMPROVED
T_BottomAlign(PassportTextInfo, 1);
T_CentreH(PassportTextInfo, 1);
}
T_RemovePrint(InvRingText);
InvRingText = NULL;
T_RemovePrint(InvItemText[0]);
InvItemText[0] = NULL;
GetSavedGamesList(&LoadGameRequester);
SetRequesterHeading(&LoadGameRequester, GF_GameStringTable[GSI_Passport_LoadGame], 0, NULL, 0);
passportMode = 1;
InputStatus = 0;
InputDB = 0;
}
}
break;
case 1 : // new game | save game | restart level
if( CHK_ANY(GF_GameFlow.flags, GFF_LoadSaveDisabled) ) {
InputDB = IN_RIGHT;
}
else if( passportMode == 1 || passportMode == 2 ) {
requester = ( passportMode == 1 ) ? &LoadGameRequester : &SaveGameRequester;
SetPassportRequesterSize(requester);
select = Display_Requester(requester, TRUE, TRUE);
if( select == 0 ) {
#ifdef FEATURE_HUD_IMPROVED
if( SavedGamesCount == 0 ) InputDB &= ~IN_LEFT;
#endif // FEATURE_HUD_IMPROVED
if( CHK_ANY(InputDB, IN_LEFT|IN_RIGHT) ) {
Remove_Requester(requester);
passportMode = 0;
} else {
InputStatus = 0;
InputDB = 0;
}
} else {
if( select > 0 ) {
InventoryExtraData[1] = select - 1;
}
passportMode = 0;
}
}
else if( passportMode == 0 ) {
if( InventoryMode == INV_DeathMode ) {
#ifdef FEATURE_HUD_IMPROVED
if( PassportTextInfo == NULL ) {
if( SavedGamesCount == 0 ) left = false;
SetPassportTextInfo(GSI_Passport_RestartLevel, left, right);
T_BottomAlign(PassportTextInfo, 1);
T_CentreH(PassportTextInfo, 1);
}
#else // FEATURE_HUD_IMPROVED
InputDB = ( item->animDirection == -1 ) ? IN_LEFT : IN_RIGHT;
#endif // FEATURE_HUD_IMPROVED
} else {
if( PassportTextInfo == NULL ) {
GAME_STRING_ID textID;
if( InventoryMode != INV_TitleMode && CurrentLevel != 0 )
textID = GSI_Passport_SaveGame;
else
textID = GSI_Passport_NewGame;
#ifdef FEATURE_HUD_IMPROVED
if( SavedGamesCount == 0 ) left = false;
SetPassportTextInfo(textID, left, right);
#else // FEATURE_HUD_IMPROVED
PassportTextInfo = T_Print(0, PASSPORT_Y_TITLE, 0, GF_GameStringTable[textID]);
#endif // FEATURE_HUD_IMPROVED
T_BottomAlign(PassportTextInfo, 1);
T_CentreH(PassportTextInfo, 1);
}
if( InventoryMode != INV_TitleMode && CurrentLevel != 0 ) {
T_RemovePrint(InvRingText);
InvRingText = NULL;
T_RemovePrint(InvItemText[0]);
InvItemText[0] = NULL;
GetSavedGamesList(&LoadGameRequester);
SetRequesterHeading(&LoadGameRequester, GF_GameStringTable[GSI_Passport_SaveGame], 0, NULL, 0);
passportMode = 1;
InputStatus = 0;
InputDB = 0;
}
else if( CHK_ANY(GF_GameFlow.flags, GFF_SelectAnyLevel) ) {
T_RemovePrint(InvItemText[0]);
InvItemText[0] = 0;
Init_Requester(&SaveGameRequester);
GetValidLevelsList(&SaveGameRequester);
SetRequesterHeading(&SaveGameRequester, GF_GameStringTable[GSI_Passport_SelectLevel], 0, NULL, 0);
passportMode = 2;
InputStatus = 0;
InputDB = 0;
}
else if( CHK_ANY(InputDB, IN_SELECT) ) {
InventoryExtraData[1] = 1;
}
}
}
break;
case 2 : // exit game | exit to title
if( PassportTextInfo == NULL ) {
GAME_STRING_ID textID;
if( InventoryMode == INV_TitleMode ) {
textID = GSI_Passport_ExitGame;
}
else if( CHK_ANY(GF_GameFlow.flags, GFF_DemoVersion) ) {
textID = GSI_Passport_ExitDemo;
}
else {
textID = GSI_Passport_ExitToTitle;
}
#ifdef FEATURE_HUD_IMPROVED
SetPassportTextInfo(textID, left, right);
#else // FEATURE_HUD_IMPROVED
PassportTextInfo = T_Print(0, PASSPORT_Y_TITLE, 0, GF_GameStringTable[textID]);
#endif // FEATURE_HUD_IMPROVED
T_BottomAlign(PassportTextInfo, 1);
T_CentreH(PassportTextInfo, 1);
}
break;
default :
break;
}
#ifdef FEATURE_HUD_IMPROVED
if( CHK_ANY(InputDB, IN_LEFT) && (InventoryMode != INV_DeathMode || SavedGamesCount != 0 || page > 1) ) {
#else // FEATURE_HUD_IMPROVED
if( CHK_ANY(InputDB, IN_LEFT) && (InventoryMode != INV_DeathMode || SavedGamesCount != 0) ) {
#endif // FEATURE_HUD_IMPROVED
item->animDirection = -1;
item->goalFrame -= 5;
if( SavedGamesCount != 0 ) {
if( item->goalFrame < item->openFrame ) {
item->goalFrame = item->openFrame;
} else {
PlaySoundEffect(115, NULL, SFX_ALWAYS); // page flip SFX
T_RemovePrint(PassportTextInfo);
PassportTextInfo = NULL;
}
} else {
if( item->goalFrame < item->openFrame + 5 ) {
item->goalFrame = item->openFrame + 5;
} else {
T_RemovePrint(PassportTextInfo);
PassportTextInfo = NULL;
}
}
InputStatus = 0;
InputDB = 0;
}
if( CHK_ANY(InputDB, IN_RIGHT) ) {
item->animDirection = 1;
item->goalFrame += 5;
if( item->goalFrame > item->framesTotal - 6 ) {
item->goalFrame = item->framesTotal - 6;
} else {
PlaySoundEffect(115, NULL, SFX_ALWAYS); // page flip SFX
T_RemovePrint(PassportTextInfo);
PassportTextInfo = NULL;
}
InputStatus = 0;
InputDB = 0;
}
if( CHK_ANY(InputDB, IN_DESELECT) ) {
if( InventoryMode == INV_DeathMode ) {
InputStatus = 0;
InputDB = 0;
} else {
if( page == 2 ) {
item->animDirection = 1;
item->goalFrame = item->framesTotal - 1;
} else {
item->animDirection = -1;
item->goalFrame = 0;
}
T_RemovePrint(PassportTextInfo);
PassportTextInfo = NULL;
}
}
if( CHK_ANY(InputDB, IN_SELECT) ) {
// NOTE: InventoryExtraData[0]=page moved from here to the line before the switch
if( page == 2 ) {
item->animDirection = 1;
item->goalFrame = item->framesTotal - 1;
} else {
item->animDirection = -1;
item->goalFrame = 0;
}
T_RemovePrint(PassportTextInfo);
PassportTextInfo = NULL;
}
}
void __cdecl do_gamma_option(INVENTORY_ITEM *item) {
// null function
}
void __cdecl do_detail_option(INVENTORY_ITEM *item) {
static TEXT_STR_INFO *DetailTextInfo[5] = {NULL};
DWORD i;
if( DetailTextInfo[0] == NULL ) {
DetailTextInfo[4] = T_Print(0, DETAIL_Y_TITLE, 0, GF_GameStringTable[GSI_Detail_SelectDetail]);
DetailTextInfo[3] = T_Print(0, DETAIL_Y_BOX, 0, " ");
DetailTextInfo[2] = T_Print(0, DETAIL_Y_LINE1, 0, GF_GameStringTable[GSI_Detail_High]);
DetailTextInfo[1] = T_Print(0, DETAIL_Y_LINE2, 0, GF_GameStringTable[GSI_Detail_Medium]);
DetailTextInfo[0] = T_Print(0, DETAIL_Y_LINE3, 0, GF_GameStringTable[GSI_Detail_Low]);
T_AddBackground(DetailTextInfo[4], DETAIL_WIDTH_M, 0, 0, 0, DETAIL_NEARZ, ICLR_Black, &ReqMainGour1, 0);
T_AddOutline(DetailTextInfo[4], TRUE, ICLR_Orange, &ReqMainGour2, 0);
T_AddBackground(DetailTextInfo[DetailLevel], DETAIL_WIDTH_S, 0, 0, 0, DETAIL_NEARZ, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(DetailTextInfo[DetailLevel], TRUE, ICLR_Orange, &ReqSelGour2, 0);
T_AddBackground(DetailTextInfo[3], DETAIL_WIDTH_L, DETAIL_HEIGHT, 0, 0, DETAIL_FARZ, ICLR_Black, &ReqBgndGour1, 0);
T_AddOutline(DetailTextInfo[3], TRUE, ICLR_Blue, &ReqBgndGour2, 0);
for( i=0; i 0 ) {
T_RemoveOutline(DetailTextInfo[DetailLevel]);
T_RemoveBackground(DetailTextInfo[DetailLevel]);
--DetailLevel;
T_AddOutline(DetailTextInfo[DetailLevel], TRUE, ICLR_Orange, &ReqSelGour2, 0);
T_AddBackground(DetailTextInfo[DetailLevel], DETAIL_WIDTH_S, 0, 0, 0, DETAIL_NEARZ, ICLR_Black, &ReqSelGour1, 0);
}
if ( CHK_ANY(InputDB, IN_FORWARD) && DetailLevel < 2 ) {
T_RemoveOutline(DetailTextInfo[DetailLevel]);
T_RemoveBackground(DetailTextInfo[DetailLevel]);
++DetailLevel;
T_AddOutline(DetailTextInfo[DetailLevel], TRUE, ICLR_Orange, &ReqSelGour2, 0);
T_AddBackground(DetailTextInfo[DetailLevel], DETAIL_WIDTH_S, 0, 0, 0, DETAIL_NEARZ, ICLR_Black, &ReqSelGour1, 0);
}
switch( DetailLevel ) {
case 2 :
// NOTE: maybe supposed to be SW_DETAIL_HIGH? Anyway this menu is disabled by Core
PerspectiveDistance = SW_DETAIL_ULTRA;
break;
case 1 :
PerspectiveDistance = SW_DETAIL_MEDIUM;
break;
case 0 :
default :
PerspectiveDistance = SW_DETAIL_LOW;
break;
}
if( CHK_ANY(InputDB, IN_SELECT|IN_DESELECT) ) {
for( i=0; i 0 ) {
#ifdef FEATURE_HUD_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode ) {
--SoundOptionLine;
} else {
T_RemoveOutline(SoundTextInfo[SoundOptionLine]);
T_RemoveBackground(SoundTextInfo[SoundOptionLine]);
--SoundOptionLine;
T_AddBackground(SoundTextInfo[SoundOptionLine], SOUND_WIDTH_S, 0, 0, 0, SOUND_NEARZ, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(SoundTextInfo[SoundOptionLine], TRUE, ICLR_Orange, &ReqSelGour2, 0);
}
#else // FEATURE_HUD_IMPROVED
T_RemoveOutline(SoundTextInfo[SoundOptionLine]);
T_RemoveBackground(SoundTextInfo[SoundOptionLine]);
--SoundOptionLine;
T_AddBackground(SoundTextInfo[SoundOptionLine], SOUND_WIDTH_S, 0, 0, 0, SOUND_NEARZ, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(SoundTextInfo[SoundOptionLine], TRUE, ICLR_Orange, &ReqSelGour2, 0);
#endif // FEATURE_HUD_IMPROVED
}
if( CHK_ANY(InputDB, IN_BACK) && SoundOptionLine < 1 ) {
#ifdef FEATURE_HUD_IMPROVED
if( SavedAppSettings.RenderMode == RM_Hardware && InvTextBoxMode ) {
++SoundOptionLine;
} else {
T_RemoveOutline(SoundTextInfo[SoundOptionLine]);
T_RemoveBackground(SoundTextInfo[SoundOptionLine]);
++SoundOptionLine;
T_AddBackground(SoundTextInfo[SoundOptionLine], SOUND_WIDTH_S, 0, 0, 0, SOUND_NEARZ, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(SoundTextInfo[SoundOptionLine], TRUE, ICLR_Orange, &ReqSelGour2, 0);
}
#else // FEATURE_HUD_IMPROVED
T_RemoveOutline(SoundTextInfo[SoundOptionLine]);
T_RemoveBackground(SoundTextInfo[SoundOptionLine]);
++SoundOptionLine;
T_AddBackground(SoundTextInfo[SoundOptionLine], SOUND_WIDTH_S, 0, 0, 0, SOUND_NEARZ, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(SoundTextInfo[SoundOptionLine], TRUE, ICLR_Orange, &ReqSelGour2, 0);
#endif // FEATURE_HUD_IMPROVED
}
switch( SoundOptionLine ) {
case 0 :
if( CHK_ANY(InputStatus, IN_LEFT) && MusicVolume > 0 )
--MusicVolume;
else if( CHK_ANY(InputStatus, IN_RIGHT) && MusicVolume < 10 )
++MusicVolume;
else
break;
IsInvOptionsDelay = TRUE;
InvOptionsDelayCounter = 5 * TICKS_PER_FRAME;
#ifdef FEATURE_HUD_IMPROVED
S_CDVolume(( MusicVolume == 0 ) ? 0 : (25 * MusicVolume + 5));
if( SavedAppSettings.RenderMode != RM_Hardware || !InvTextBoxMode ) {
sprintf(volumeString, "%2d", MusicVolume);
T_ChangeText(SoundTextInfo[6], volumeString);
PlaySoundEffect(115, NULL, SFX_ALWAYS); // page flip SFX
}
#else // FEATURE_HUD_IMPROVED
sprintf(volumeString, "| %2d", MusicVolume); // Char '|' is musical note picture
T_ChangeText(SoundTextInfo[0], volumeString);
S_CDVolume(( MusicVolume == 0 ) ? 0 : (25 * MusicVolume + 5));
PlaySoundEffect(115, NULL, SFX_ALWAYS); // page flip SFX
#endif // FEATURE_HUD_IMPROVED
break;
case 1 :
if( CHK_ANY(InputStatus, IN_LEFT) && SoundVolume > 0 )
--SoundVolume;
else if( CHK_ANY(InputStatus, IN_RIGHT) && SoundVolume < 10 )
++SoundVolume;
else
break;
IsInvOptionsDelay = TRUE;
InvOptionsDelayCounter = 5 * TICKS_PER_FRAME;
#ifdef FEATURE_HUD_IMPROVED
S_SoundSetMasterVolume(( SoundVolume == 0 ) ? 0 : (6 * SoundVolume + 4));
if( SavedAppSettings.RenderMode != RM_Hardware || !InvTextBoxMode ) {
sprintf(volumeString, "%2d", SoundVolume);
T_ChangeText(SoundTextInfo[7], volumeString);
PlaySoundEffect(115, NULL, SFX_ALWAYS); // page flip SFX
}
#else // FEATURE_HUD_IMPROVED
sprintf(volumeString, "} %2d", SoundVolume); // Char '}' is dynamic speaker picture
T_ChangeText(SoundTextInfo[1], volumeString);
S_SoundSetMasterVolume(( SoundVolume == 0 ) ? 0 : (6 * SoundVolume + 4));
PlaySoundEffect(115, NULL, SFX_ALWAYS); // page flip SFX
#endif // FEATURE_HUD_IMPROVED
break;
default :
break;
}
#ifndef FEATURE_HUD_IMPROVED
// NOTE: this is leftover from the original code. The code is unused
InvSpriteSoundVolume[6].param1 = SoundVolume;
InvSpriteSoundVolumeLow[6].param1 = SoundVolume;
InvSpriteMusicVolume[6].param1 = MusicVolume;
InvSpriteMusicVolumeLow[6].param1 = MusicVolume;
#endif // FEATURE_HUD_IMPROVED
if( CHK_ANY(InputDB, IN_SELECT|IN_DESELECT) ) {
for( i=0; ianimDirection = 1;
item->goalFrame = item->framesTotal - 1;
}
PlaySoundEffect(113, NULL, SFX_ALWAYS); // ticking clock sound
}
void __cdecl FlashConflicts() {
UINT16 key;
DWORD skip = 0;
#ifdef FEATURE_HUD_IMPROVED
if( LayoutPage == CTRL_Joystick ) skip = 4;
#endif // FEATURE_HUD_IMPROVED
for( DWORD i=0; izPos = 0;
T_AddBackground(CtrlTextA[KeyCursor], 0, 0, 0, 0, 0, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(CtrlTextA[KeyCursor], TRUE, ICLR_Blue, &ReqSelGour2, 0);
}
#else // FEATURE_HUD_IMPROVED
KeyCursor = -1;
T_AddBackground(ControlTextInfo[0], 0, 0, 0, 0, CONTROL_FARZ, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(ControlTextInfo[0], TRUE, ICLR_Blue, &ReqSelGour2, 0);
#endif // FEATURE_HUD_IMPROVED
FlashConflicts(); // NOTE: this line is absent in the original game
}
switch( KeySelector ) {
case 0 :
if( CHK_ANY(InputDB, IN_LEFT|IN_RIGHT) ) {
if( KeyCursor == -1 ) {
LayoutPage = !LayoutPage;
S_ChangeCtrlText();
FlashConflicts();
} else {
CtrlTextA[KeyCursor]->zPos = CONTROL_NEARZ;
T_RemoveBackground(CtrlTextA[KeyCursor]);
T_RemoveOutline(CtrlTextA[KeyCursor]);
if( KeyCursor < (int)CONTROL_LINE_COUNT ) {
KeyCursor += CONTROL_LINE_COUNT;
} else if ( KeyCursor < (int)CONTROL_LINE_COUNT*2 ) {
KeyCursor -= CONTROL_LINE_COUNT;
} else {
KeyCursor = CONTROL_LINE_COUNT;
}
#ifdef FEATURE_HUD_IMPROVED
while( KeyCursor >= 0 && !*CtrlTextA[KeyCursor]->pString ) {
--KeyCursor;
}
if( KeyCursor == -1 ) {
while( ++KeyCursor < (int)CONTROL_LINE_COUNT*2 && !*CtrlTextA[KeyCursor]->pString ) {}
}
T_ChangeText(ControlTextInfo[0], GetControlsHeaderString());
#endif // FEATURE_HUD_IMPROVED
CtrlTextA[KeyCursor]->zPos = 0;
T_AddBackground(CtrlTextA[KeyCursor], 0, 0, 0, 0, 0, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(CtrlTextA[KeyCursor], TRUE, ICLR_Blue, &ReqSelGour2, 0);
}
}
else if( (CHK_ANY(InputDB, IN_DESELECT) && !CHK_ANY(InputDB, IN_SELECT)) || (CHK_ANY(InputDB, IN_SELECT) && KeyCursor == -1) ) {
T_RemovePrint(ControlTextInfo[0]);
ControlTextInfo[0] = NULL;
T_RemovePrint(ControlTextInfo[1]);
ControlTextInfo[1] = NULL;
S_RemoveCtrlText();
DefaultConflict();
return;
}
#ifndef FEATURE_HUD_IMPROVED
if( LayoutPage == CTRL_Default )
break;
#endif // FEATURE_HUD_IMPROVED
if( CHK_ANY(InputDB, IN_SELECT) ) {
#ifdef FEATURE_HUD_IMPROVED
if( LayoutPage == CTRL_Joystick && KeyCursor >= 0 && KeyCursor < 4 ) {
T_ChangeText(CtrlTextB[KeyCursor], JoystickOpts[KeyCursor].toggle());
break;
}
UpdateJoystickHintText(HINT_HIDDEN, HINT_KEYBOARD, HINT_KEYBOARD);
#endif // FEATURE_HUD_IMPROVED
KeySelector = 1;
CtrlTextA[KeyCursor]->zPos = CONTROL_NEARZ;
T_RemoveBackground(CtrlTextA[KeyCursor]);
T_RemoveOutline(CtrlTextA[KeyCursor]);
CtrlTextB[KeyCursor]->zPos = 0;
T_AddBackground(CtrlTextB[KeyCursor], 0, 0, 0, 0, 0, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(CtrlTextB[KeyCursor], TRUE, ICLR_Blue, &ReqSelGour2, 0);
}
else if( CHK_ANY(InputDB, IN_FORWARD) ) {
if( KeyCursor == -1 ) {
T_RemoveBackground(ControlTextInfo[0]);
T_RemoveOutline(ControlTextInfo[0]);
} else {
CtrlTextA[KeyCursor]->zPos = CONTROL_NEARZ;
T_RemoveBackground(CtrlTextA[KeyCursor]);
T_RemoveOutline(CtrlTextA[KeyCursor]);
}
if( --KeyCursor < -1 ) {
KeyCursor = CONTROL_LINE_COUNT*2 - 1;
}
#ifdef FEATURE_HUD_IMPROVED
while( KeyCursor >= 0 && !*CtrlTextA[KeyCursor]->pString ) {
--KeyCursor;
}
if( isJoystickUnavailable && KeyCursor == -1 ) {
KeyCursor = CONTROL_LINE_COUNT*2 - 1;
while( KeyCursor >= 0 && !*CtrlTextA[KeyCursor]->pString ) {
--KeyCursor;
}
}
T_ChangeText(ControlTextInfo[0], GetControlsHeaderString());
#endif // FEATURE_HUD_IMPROVED
if( KeyCursor == -1 ) {
T_AddBackground(ControlTextInfo[0], 0, 0, 0, 0, 0, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(ControlTextInfo[0], TRUE, ICLR_Blue, &ReqSelGour2, 0);
} else {
CtrlTextA[KeyCursor]->zPos = 0;
T_AddBackground(CtrlTextA[KeyCursor], 0, 0, 0, 0, 0, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(CtrlTextA[KeyCursor], TRUE, ICLR_Blue, &ReqSelGour2, 0);
}
}
else if( CHK_ANY(InputDB, IN_BACK) ) {
if( KeyCursor == -1 ) {
T_RemoveBackground(ControlTextInfo[0]);
T_RemoveOutline(ControlTextInfo[0]);
} else {
CtrlTextA[KeyCursor]->zPos = CONTROL_NEARZ;
T_RemoveBackground(CtrlTextA[KeyCursor]);
T_RemoveOutline(CtrlTextA[KeyCursor]);
}
#ifdef FEATURE_HUD_IMPROVED
++KeyCursor;
while( KeyCursor < (int)CONTROL_LINE_COUNT*2 && !*CtrlTextA[KeyCursor]->pString ) {
++KeyCursor;
}
if( KeyCursor >= (int)CONTROL_LINE_COUNT*2 ) {
if( isJoystickUnavailable ) {
KeyCursor = 0;
while( KeyCursor < (int)CONTROL_LINE_COUNT*2 && !*CtrlTextA[KeyCursor]->pString ) {
++KeyCursor;
}
} else {
KeyCursor = -1;
}
}
T_ChangeText(ControlTextInfo[0], GetControlsHeaderString());
#else // FEATURE_HUD_IMPROVED
if( ++KeyCursor >= (int)CONTROL_LINE_COUNT*2 ) {
KeyCursor = -1;
}
#endif // FEATURE_HUD_IMPROVED
if( KeyCursor == -1 ) {
T_AddBackground(ControlTextInfo[0], 0, 0, 0, 0, 0, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(ControlTextInfo[0], TRUE, ICLR_Blue, &ReqSelGour2, 0);
} else {
CtrlTextA[KeyCursor]->zPos = 0;
T_AddBackground(CtrlTextA[KeyCursor], 0, 0, 0, 0, 0, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(CtrlTextA[KeyCursor], TRUE, ICLR_Blue, &ReqSelGour2, 0);
}
}
break;
case 1:
// NOTE: InputDB replaced by InputStatus here. This fixes the original game bug
if( !CHK_ANY(InputStatus, IN_SELECT) && !CHK_ANY(DIKeys[DIK_RETURN], 0x80) ) {
KeySelector = 2;
}
break;
case 2:
#ifdef FEATURE_HUD_IMPROVED
i = 0;
if( LayoutPage == CTRL_Joystick && CHK_ANY(JoyKeys, 0xFFFF) ) {
for( i = 0; i < 0x10; ++i ) {
if( CHK_ANY((1 << i), JoyKeys) )
break;
}
if( i == 0x10 ) {
break;
}
i += 0x100;
} else if( LayoutPage == CTRL_Custom || CHK_ANY(DIKeys[DIK_ESCAPE], 0x80) )
#else // FEATURE_HUD_IMPROVED
if( JoyKeys != 0 ) {
for( i = 0; i < 0x20; ++i ) {
if( CHK_ANY((1 << i), JoyKeys) )
break;
}
if( i == 0x20 ) {
break;
}
i += 0x100;
} else
#endif // FEATURE_HUD_IMPROVED
{
for( i = 0; i < 0x100; ++i ) {
if( CHK_ANY(DIKeys[i], 0x80) )
break;
}
if( i == 0x100 ) {
break;
}
}
if( i != 0 && ControlKeysText[i] != NULL && i != DIK_RETURN &&
i != DIK_LEFT && i != DIK_RIGHT && i != DIK_UP && i != DIK_DOWN )
{
if( i != DIK_ESCAPE ) {
#ifdef FEATURE_HUD_IMPROVED
Layout[LayoutPage].key[KeyCursor] = (i >= 0x100) ? i-0x100 : i;
if( Layout[LayoutPage].key[KeyCursor] == DIK_LCONTROL )
Layout[LayoutPage].key[KeyCursor] = DIK_RCONTROL;
if( Layout[LayoutPage].key[KeyCursor] == DIK_LSHIFT )
Layout[LayoutPage].key[KeyCursor] = DIK_RSHIFT;
if( Layout[LayoutPage].key[KeyCursor] == DIK_LMENU )
Layout[LayoutPage].key[KeyCursor] = DIK_RMENU;
DefaultConflict();
S_ChangeCtrlText();
#else // FEATURE_HUD_IMPROVED
Layout[LayoutPage].key[KeyCursor] = i;
T_ChangeText(CtrlTextB[KeyCursor], ControlKeysText[i]);
#endif // FEATURE_HUD_IMPROVED
}
CtrlTextB[KeyCursor]->zPos = CONTROL_NEARZ;
T_RemoveBackground(CtrlTextB[KeyCursor]);
T_RemoveOutline(CtrlTextB[KeyCursor]);
CtrlTextA[KeyCursor]->zPos = 0;
T_AddBackground(CtrlTextA[KeyCursor], 0, 0, 0, 0, 0, ICLR_Black, &ReqSelGour1, 0);
T_AddOutline(CtrlTextA[KeyCursor], TRUE, ICLR_Blue, &ReqSelGour2, 0);
KeySelector = 3;
FlashConflicts();
}
break;
case 3:
#ifdef FEATURE_HUD_IMPROVED
if( LayoutPage == CTRL_Joystick )
#else // FEATURE_HUD_IMPROVED
if( CHK_ANY(Layout[LayoutPage].key[KeyCursor], 0x100) )
#endif // FEATURE_HUD_IMPROVED
{
if( !CHK_ANY((1 << Layout[LayoutPage].key[KeyCursor]), JoyKeys) ) {
KeySelector = 0;
}
}
else if( !CHK_ANY(DIKeys[Layout[LayoutPage].key[KeyCursor]], 0x80) ) {
KeySelector = 0;
#ifndef FEATURE_HUD_IMPROVED
if( Layout[LayoutPage].key[KeyCursor] == DIK_LCONTROL )
Layout[LayoutPage].key[KeyCursor] = DIK_RCONTROL;
if( Layout[LayoutPage].key[KeyCursor] == DIK_LSHIFT )
Layout[LayoutPage].key[KeyCursor] = DIK_RSHIFT;
if( Layout[LayoutPage].key[KeyCursor] == DIK_LMENU )
Layout[LayoutPage].key[KeyCursor] = DIK_RMENU;
FlashConflicts();
#endif // FEATURE_HUD_IMPROVED
}
#ifdef FEATURE_HUD_IMPROVED
if( KeySelector == 0 ) {
UpdateJoystickHintText(HINT_JOYSTICK, HINT_JOYSTICK, HINT_JOYSTICK);
}
#endif // FEATURE_HUD_IMPROVED
break;
}
InputStatus = 0;
InputDB = 0;
}
void __cdecl S_ShowControls() {
DWORD i;
int x0, x1, xCenter;
bool isCompact;
#ifdef FEATURE_HUD_IMPROVED
xCenter = GetRenderWidthDownscaled() / 2;
isCompact = (xCenter < (CONTROL_WIDTH_HIGH + 20) / 2);
if( CtrlTextC[0] == NULL ) {
x1 = CONTROL_COLUMN_C;
x0 = x1 - (isCompact ? CONTROL_WIDTH_LOW : CONTROL_WIDTH_HIGH) / 2;
CtrlTextC[0] = T_Print(x0, CONTROL_Y_LINE1, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[0]]);
CtrlTextC[1] = T_Print(x0, CONTROL_Y_LINE2, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[1]]);
CtrlTextC[2] = T_Print(x0, CONTROL_Y_LINE3, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[2]]);
CtrlTextC[3] = T_Print(x0, CONTROL_Y_LINE4, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[3]]);
CtrlTextC[4] = T_Print(x0, CONTROL_Y_LINE5, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[4]]);
CtrlTextC[5] = T_Print(x0, CONTROL_Y_LINE6, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[5]]);
CtrlTextC[6] = T_Print(x0, CONTROL_Y_LINE7, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[6]]);
CtrlTextC[7] = T_Print(x0, CONTROL_Y_LINE8, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[7]]);
CtrlTextC[8] = T_Print(x1, CONTROL_Y_LINE1, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[8]]);
CtrlTextC[9] = T_Print(x1, CONTROL_Y_LINE2, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[9]]);
CtrlTextC[10] = T_Print(x1, CONTROL_Y_LINE3, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[10]]);
CtrlTextC[11] = T_Print(x1, CONTROL_Y_LINE4, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[11]]);
CtrlTextC[12] = T_Print(x1, CONTROL_Y_LINE5, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[12]]);
CtrlTextC[13] = T_Print(x1, CONTROL_Y_LINE6, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[13]]);
CtrlTextC[14] = T_Print(x1, CONTROL_Y_LINE7, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[14]]);
CtrlTextC[15] = T_Print(x1, CONTROL_Y_LINE8, CONTROL_NEARZ, ControlKeysText[Layout[CTRL_Default].key[15]]);
for( i=0; ixPos = (i < CONTROL_LINE_COUNT) ? x0 : x1;
}
T_CentreH(CtrlTextB[i], 1);
T_CentreV(CtrlTextB[i], 1);
}
#else // FEATURE_HUD_IMPROVED
x0 = xCenter - (isCompact ? CONTROL_WIDTH_LOW : CONTROL_WIDTH_HIGH) / 2 + CONTROL_COLUMN_B;
x1 = xCenter + CONTROL_COLUMN_B;
CtrlTextB[0] = T_Print(x0, CONTROL_Y_LINE1, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[0]]);
CtrlTextB[1] = T_Print(x0, CONTROL_Y_LINE2, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[1]]);
CtrlTextB[2] = T_Print(x0, CONTROL_Y_LINE3, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[2]]);
CtrlTextB[3] = T_Print(x0, CONTROL_Y_LINE4, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[3]]);
CtrlTextB[4] = T_Print(x0, CONTROL_Y_LINE5, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[4]]);
CtrlTextB[5] = T_Print(x0, CONTROL_Y_LINE6, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[5]]);
CtrlTextB[6] = T_Print(x0, CONTROL_Y_LINE7, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[6]]);
CtrlTextB[7] = T_Print(x1, CONTROL_Y_LINE1, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[7]]);
CtrlTextB[8] = T_Print(x1, CONTROL_Y_LINE2, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[8]]);
CtrlTextB[9] = T_Print(x1, CONTROL_Y_LINE3, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[9]]);
CtrlTextB[10] = T_Print(x1, CONTROL_Y_LINE4, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[10]]);
CtrlTextB[11] = T_Print(x1, CONTROL_Y_LINE5, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[11]]);
CtrlTextB[12] = T_Print(x1, CONTROL_Y_LINE6, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[12]]);
CtrlTextB[13] = T_Print(x1, CONTROL_Y_LINE7, CONTROL_NEARZ, ControlKeysText[Layout[LayoutPage].key[13]]);
for( i=0; ixPos = (i < CONTROL_LINE_COUNT) ? x0 : x1;
T_CentreH(CtrlTextB[i], 1);
}
for( DWORD i=(LayoutPage == CTRL_Joystick)? 4 : 0; i .
*/
#ifndef OPTION_H_INCLUDED
#define OPTION_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl do_inventory_options(INVENTORY_ITEM *item); // 0x0044EE80
void __cdecl do_passport_option(INVENTORY_ITEM *item); // 0x0044EF90
void __cdecl do_gamma_option(INVENTORY_ITEM *item); // ----------
void __cdecl do_detail_option(INVENTORY_ITEM *item); // 0x0044F5E0
void __cdecl do_sound_option(INVENTORY_ITEM *item); // 0x0044F8C0
void __cdecl do_compass_option(INVENTORY_ITEM *item); // 0x0044FD60
void __cdecl FlashConflicts(); // 0x0044FE20
void __cdecl DefaultConflict(); // 0x0044FEA0
void __cdecl do_control_option(INVENTORY_ITEM *item); // 0x0044FEE0
void __cdecl S_ShowControls(); // 0x004505F0
void __cdecl S_ChangeCtrlText(); // 0x00450AC0
void __cdecl S_RemoveCtrlText(); // 0x00450B60
#endif // OPTION_H_INCLUDED
================================================
FILE: specific/output.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/output.h"
#include "3dsystem/3d_gen.h"
#include "3dsystem/phd_math.h"
#include "game/gameflow.h"
#include "specific/background.h"
#include "specific/display.h"
#include "specific/file.h"
#include "specific/game.h"
#include "specific/hwr.h"
#include "specific/init.h"
#include "specific/init_display.h"
#include "specific/input.h"
#include "specific/texture.h"
#include "specific/utils.h"
#include "specific/winvid.h"
#include "global/vars.h"
#ifdef FEATURE_HUD_IMPROVED
#include "modding/psx_bar.h"
DWORD HealthBarMode = 2;
bool PsxBarPosEnabled = true;
double GameGUI_Scale = 1.0;
double InvGUI_Scale = 1.0;
#endif // FEATURE_HUD_IMPROVED
#ifdef FEATURE_VIEW_IMPROVED
extern int CalculateFogShade(int depth);
#endif // FEATURE_VIEW_IMPROVED
#ifdef FEATURE_VIDEOFX_IMPROVED
DWORD ShadowMode = 1;
#endif // FEATURE_VIDEOFX_IMPROVED
#ifdef FEATURE_BACKGROUND_IMPROVED
#include "modding/background_new.h"
extern DWORD BGND_PictureWidth;
extern DWORD BGND_PictureHeight;
extern bool BGND_IsCaptured;
extern DWORD InvBackgroundMode;
extern DWORD StatsBackgroundMode;
extern bool IsFadeToBlack;
DWORD PauseBackgroundMode = 1;
#endif // FEATURE_BACKGROUND_IMPROVED
typedef struct ShadowInfo_t {
__int16 x;
__int16 y;
__int16 z;
__int16 radius;
__int16 polyCount;
__int16 vertexCount;
POS_3D vertex[32]; // original size was 8
} SHADOW_INFO;
// NOTE: there was no such backup in the original game
PHD_TEXTURE TextureBackupUV[ARRAY_SIZE(PhdTextureInfo)];
#if (DIRECT3D_VERSION >= 0x900)
static bool SWR_StretchBlt(SWR_BUFFER *dstBuf, RECT *dstRect, SWR_BUFFER *srcBuf, RECT *srcRect) {
if( !srcBuf || !srcBuf->bitmap || !srcBuf->width || !srcBuf->height ||
!dstBuf || !dstBuf->bitmap || !dstBuf->width || !dstBuf->height )
{
return false;
}
int sx = 0;
int sy = 0;
int sw = srcBuf->width;
int sh = srcBuf->height;
if( srcRect ) {
sx = srcRect->left;
sy = srcRect->top;
sw = srcRect->right;
sh = srcRect->bottom;
CLAMP(sx, 0, (int)srcBuf->width);
CLAMP(sy, 0, (int)srcBuf->height);
CLAMP(sw, 0, (int)srcBuf->width);
CLAMP(sh, 0, (int)srcBuf->height);
sw -= sx;
sh -= sy;
if( !sw || !sh ) return false;
}
int dx = 0;
int dy = 0;
int dw = dstBuf->width;
int dh = dstBuf->height;
if( dstRect ) {
dx = dstRect->left;
dy = dstRect->top;
dw = dstRect->right;
dh = dstRect->bottom;
CLAMP(dx, 0, (int)dstBuf->width);
CLAMP(dy, 0, (int)dstBuf->height);
CLAMP(dw, 0, (int)dstBuf->width);
CLAMP(dh, 0, (int)dstBuf->height);
dw -= dx;
dh -= dy;
if( !dw || !dh ) return false;
}
if( dw < 0 ) {
dx += dw;
dw = -dw;
sx += sw;
sw = -sw;
}
if( dh < 0 ) {
dy += dh;
dh = -dh;
sy += sh;
sh = -sh;
}
int *x = (int *)malloc(sizeof(int) * dw);
if( !x ) return false;
for( int i = 0; i < dw; ++i ) {
x[i] = i * sw / dw;
}
for( int j = 0; j < dh; ++j ) {
int y = j * sh / dh;
LPBYTE src = srcBuf->bitmap + srcBuf->width * y + sx;
LPBYTE dst = dstBuf->bitmap + dstBuf->width * j + dx;
for( int i = 0; i < dw; ++i ) {
dst[i] = src[x[i]];
}
}
free(x);
return true;
}
#endif // (DIRECT3D_VERSION >= 0x900)
int __cdecl GetRenderScale(int unit) {
#ifdef FEATURE_HUD_IMPROVED
int baseWidth = 640 / (IsVidSizeLock ? InvGUI_Scale : GameGUI_Scale);
int baseHeight = 480 / (IsVidSizeLock ? InvGUI_Scale : GameGUI_Scale);
#else // !FEATURE_HUD_IMPROVED
int baseWidth = 640;
int baseHeight = 480;
#endif // FEATURE_HUD_IMPROVED
int scaleX = (PhdWinWidth > baseWidth) ? MulDiv(PhdWinWidth, unit, baseWidth) : unit;
int scaleY = (PhdWinHeight > baseHeight) ? MulDiv(PhdWinHeight, unit, baseHeight) : unit;
return MIN(scaleX, scaleY);
}
int __cdecl GetRenderHeightDownscaled() {
return PhdWinHeight * PHD_ONE / GetRenderScale(PHD_ONE);
}
int __cdecl GetRenderWidthDownscaled() {
return PhdWinWidth * PHD_ONE / GetRenderScale(PHD_ONE);
}
int __cdecl GetRenderHeight() {
return PhdWinHeight;
}
int __cdecl GetRenderWidth() {
return PhdWinWidth;
}
void __cdecl S_InitialisePolyList(BOOL clearBackBuffer) {
DWORD flags = 0;
if( WinVidNeedToResetBuffers ) {
#if (DIRECT3D_VERSION < 0x900)
RestoreLostBuffers();
#endif // (DIRECT3D_VERSION < 0x900)
WinVidSpinMessageLoop(false);
if( SavedAppSettings.FullScreen ) {
flags = CLRB_BackBuffer|CLRB_PrimaryBuffer;
if( SavedAppSettings.RenderMode == RM_Software )
flags |= CLRB_RenderBuffer;
if( SavedAppSettings.TripleBuffering )
flags |= CLRB_ThirdBuffer;
#if (DIRECT3D_VERSION < 0x900)
WaitPrimaryBufferFlip();
#endif // (DIRECT3D_VERSION < 0x900)
} else {
flags = CLRB_WindowedPrimaryBuffer;
if( SavedAppSettings.RenderMode == RM_Hardware )
flags |= CLRB_BackBuffer;
else
flags |= CLRB_RenderBuffer;
}
ClearBuffers(flags, 0);
clearBackBuffer = FALSE; // already cleared here
WinVidNeedToResetBuffers = false;
}
flags = CLRB_PhdWinSize;
if( SavedAppSettings.RenderMode == RM_Software ) {
// Software Renderer
#if (DIRECT3D_VERSION >= 0x900)
if( clearBackBuffer )
flags |= CLRB_BackBuffer|CLRB_RenderBuffer;
#else // (DIRECT3D_VERSION >= 0x900)
flags |= CLRB_RenderBuffer;
#endif // (DIRECT3D_VERSION >= 0x900)
ClearBuffers(flags, 0);
} else {
// Hardware Renderer
if( clearBackBuffer )
flags |= CLRB_BackBuffer;
#if (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.ZBuffer )
flags |= CLRB_ZBuffer;
#else // (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.ZBuffer && ZBufferSurface != NULL )
flags |= CLRB_ZBuffer;
#endif // (DIRECT3D_VERSION >= 0x900)
ClearBuffers(flags, 0);
HWR_BeginScene();
HWR_EnableZBuffer(true, true);
}
phd_InitPolyList();
#if defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION < 0x900)
FreeEnvmapTexture();
#endif // defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION < 0x900)
}
DWORD __cdecl S_DumpScreen() {
DWORD ticks = SyncTicks(TICKS_PER_FRAME); // NOTE: there was another code in the original game
ScreenPartialDump();
return ticks;
}
void __cdecl S_ClearScreen() {
ScreenClear(false);
}
void __cdecl S_InitialiseScreen(GF_LEVEL_TYPE levelType) {
if( levelType < 0 ) {
// No Level
FadeToPal(0, GamePalette8);
} else {
if( levelType != GFL_TITLE ) {
// Not title
TempVideoRemove();
}
// Title or Any Level
FadeToPal(30, GamePalette8);
}
if( SavedAppSettings.RenderMode == RM_Hardware ) {
HWR_InitState();
}
}
void __cdecl S_OutputPolyList() {
DDSDESC desc;
if( SavedAppSettings.RenderMode == RM_Software ) {
// Software renderer
phd_SortPolyList();
#if (DIRECT3D_VERSION >= 0x900)
// prefetch surface lock
extern LPDDS CaptureBufferSurface;
HRESULT rc = CaptureBufferSurface->LockRect(&desc, NULL, D3DLOCK_DONOTWAIT);
if( FAILED(rc) && rc != D3DERR_WASSTILLDRAWING ) {
return;
}
// do software rendering
extern void PrepareSWR(int pitch, int height);
PrepareSWR(RenderBuffer.width, RenderBuffer.height);
phd_PrintPolyList(RenderBuffer.bitmap);
// finish surface lock
if( rc == D3DERR_WASSTILLDRAWING && FAILED(CaptureBufferSurface->LockRect(&desc, NULL, 0)) ) {
return;
}
// copy bitmap to surface
BYTE *src = RenderBuffer.bitmap;
for( DWORD i=0; iUnlockRect();
#else // (DIRECT3D_VERSION >= 0x900)
if SUCCEEDED(WinVidBufferLock(RenderBufferSurface, &desc, DDLOCK_WRITEONLY|DDLOCK_WAIT)) {
#ifdef FEATURE_NOLEGACY_OPTIONS
extern void PrepareSWR(int pitch, int height);
PrepareSWR(desc.lPitch, desc.dwHeight);
#endif // FEATURE_NOLEGACY_OPTIONS
phd_PrintPolyList((BYTE *)desc.lpSurface);
WinVidBufferUnlock(RenderBufferSurface, &desc);
}
#endif // (DIRECT3D_VERSION >= 0x900)
} else {
// Hardware renderer
if( !SavedAppSettings.ZBuffer || !SavedAppSettings.DontSortPrimitives ) {
phd_SortPolyList();
}
HWR_DrawPolyList();
D3DDev->EndScene();
}
}
int __cdecl S_GetObjectBounds(__int16 *bPtr) {
int xMin, xMax, yMin, yMax, zMin, zMax;
int numZ, xv, yv, zv;
PHD_VECTOR vtx[8];
if( PhdMatrixPtr->_23 >= PhdFarZ )
return 0; // object box is out of screen
xMin = bPtr[0];
xMax = bPtr[1];
yMin = bPtr[2];
yMax = bPtr[3];
zMin = bPtr[4];
zMax = bPtr[5];
vtx[0].x = xMin; vtx[0].y = yMin; vtx[0].z = zMin;
vtx[1].x = xMax; vtx[1].y = yMin; vtx[1].z = zMin;
vtx[2].x = xMax; vtx[2].y = yMax; vtx[2].z = zMin;
vtx[3].x = xMin; vtx[3].y = yMax; vtx[3].z = zMin;
vtx[4].x = xMin; vtx[4].y = yMin; vtx[4].z = zMax;
vtx[5].x = xMax; vtx[5].y = yMin; vtx[5].z = zMax;
vtx[6].x = xMax; vtx[6].y = yMax; vtx[6].z = zMax;
vtx[7].x = xMin; vtx[7].y = yMax; vtx[7].z = zMax;
xMin = yMin = +0x3FFFFFFF;
xMax = yMax = -0x3FFFFFFF;
numZ = 0;
for( int i=0; i<8; ++i ) {
zv = PhdMatrixPtr->_20 * vtx[i].x +
PhdMatrixPtr->_21 * vtx[i].y +
PhdMatrixPtr->_22 * vtx[i].z +
PhdMatrixPtr->_23;
if( zv > PhdNearZ && zv < PhdFarZ ) {
++numZ;
zv /= PhdPersp;
xv = (PhdMatrixPtr->_00 * vtx[i].x +
PhdMatrixPtr->_01 * vtx[i].y +
PhdMatrixPtr->_02 * vtx[i].z +
PhdMatrixPtr->_03) / zv;
if( xMin > xv )
xMin = xv;
if( xMax < xv )
xMax = xv;
yv = (PhdMatrixPtr->_10 * vtx[i].x +
PhdMatrixPtr->_11 * vtx[i].y +
PhdMatrixPtr->_12 * vtx[i].z +
PhdMatrixPtr->_13) / zv;
if( yMin > yv )
yMin = yv;
if( yMax < yv )
yMax = yv;
}
}
xMin += PhdWinCenterX;
xMax += PhdWinCenterX;
yMin += PhdWinCenterY;
yMax += PhdWinCenterY;
if( numZ == 0 || xMin > PhdWinRight || yMin > PhdWinBottom || xMax < PhdWinLeft || yMax < PhdWinTop )
return 0; // object box is out of screen
if ( numZ < 8 || xMin < 0 || yMin < 0 || xMax > PhdWinMaxX || yMax > PhdWinMaxY )
return -1; // object box is clipped
return 1; // object box is totally on screen
}
void __cdecl S_InsertBackPolygon(int x0, int y0, int x1, int y1) {
ins_flat_rect(PhdWinMinX + x0, PhdWinMinY + y0,
PhdWinMinX + x1, PhdWinMinY + y1,
PhdFarZ + 1, InvColours[ICLR_Black]);
}
void __cdecl S_PrintShadow(__int16 radius, __int16 *bPtr, ITEM_INFO *item) {
static SHADOW_INFO ShadowInfo = {
.x = 0,
.y = 0,
.z = 0,
.radius = 0x7FFF,
.polyCount = 1,
.vertexCount = 8,
.vertex = {{0,0,0}},
};
int x0, x1, z0, z1, midX, midZ, xAdd, zAdd;
x0 = bPtr[0];
x1 = bPtr[1];
z0 = bPtr[4];
z1 = bPtr[5];
midX = (x0 + x1) / 2;
xAdd = (x1 - x0) * radius / 0x400;
midZ = (z0 + z1) / 2;
zAdd = (z1 - z0) * radius / 0x400;
#ifdef FEATURE_VIDEOFX_IMPROVED
if( ShadowMode == 1 ) {
// The shadow is a circle
ShadowInfo.vertexCount = 32;
for( int i = 0; i < ShadowInfo.vertexCount; ++i ) {
int angle = (PHD_180 + i * PHD_360) / ShadowInfo.vertexCount;
ShadowInfo.vertex[i].x = midX + (xAdd * 2) * phd_sin(angle) / PHD_IONE;
ShadowInfo.vertex[i].z = midZ + (zAdd * 2) * phd_cos(angle) / PHD_IONE;
}
} else
#endif // FEATURE_VIDEOFX_IMPROVED
{
// The shadow is an octagon
ShadowInfo.vertexCount = 8;
ShadowInfo.vertex[0].x = midX - xAdd;
ShadowInfo.vertex[0].z = midZ + zAdd * 2;
ShadowInfo.vertex[1].x = midX + xAdd;
ShadowInfo.vertex[1].z = midZ + zAdd * 2;
ShadowInfo.vertex[2].x = midX + xAdd * 2;
ShadowInfo.vertex[2].z = midZ + zAdd;
ShadowInfo.vertex[3].x = midX + xAdd * 2;
ShadowInfo.vertex[3].z = midZ - zAdd;
ShadowInfo.vertex[4].x = midX + xAdd;
ShadowInfo.vertex[4].z = midZ - zAdd * 2;
ShadowInfo.vertex[5].x = midX - xAdd;
ShadowInfo.vertex[5].z = midZ - zAdd * 2;
ShadowInfo.vertex[6].x = midX - xAdd * 2;
ShadowInfo.vertex[6].z = midZ - zAdd;
ShadowInfo.vertex[7].x = midX - xAdd * 2;
ShadowInfo.vertex[7].z = midZ + zAdd;
}
// Update screen parameters
FltWinLeft = (double)(PhdWinMinX + PhdWinLeft);
FltWinTop = (double)(PhdWinMinY + PhdWinTop);
FltWinRight = (double)(PhdWinMinX + PhdWinRight+1);
FltWinBottom = (double)(PhdWinMinY + PhdWinBottom+1);
FltWinCenterX = (double)(PhdWinMinX + PhdWinCenterX);
FltWinCenterY = (double)(PhdWinMinY + PhdWinCenterY);
// Transform and print the shadow
phd_PushMatrix();
phd_TranslateAbs(item->pos.x, item->floor, item->pos.z);
phd_RotY(item->pos.rotY);
if( calc_object_vertices(&ShadowInfo.polyCount) ) {
// NOTE: Here 24 is DepthQ index (shade factor).
// 0 lightest, 15 no shade, 31 darkest (pitch black).
// But original code has value 32 supposed to be interpreted as 24 (which means 50% darker)
// Also 32 is maximum valid value in the original code, though it is DepthQTable range violation.
// This trick worked because DepthQIndex array was right after DepthQ array in the memory
// (DepthQIndex is equal to &DepthQ[24].index).This allocation is not guaranteed on some systems, so it was fixed
ins_poly_trans8(PhdVBuf, 24);
}
phd_PopMatrix();
}
void __cdecl S_CalculateLight(int x, int y, int z, __int16 roomNumber) {
ROOM_INFO *room;
int xDist, yDist, zDist, distance, radius, depth;
int xBrightest=0, yBrightest=0, zBrightest=0;
int brightest, adder;
int shade, shade1, shade2;
int falloff, falloff1, falloff2;
int intensity, intensity1, intensity2;
int lightShade;
VECTOR_ANGLES angles;
room = &RoomInfo[roomNumber];
brightest = 0;
// Static light calculation
if( room->lightMode != 0 ) {
lightShade = RoomLightShades[room->lightMode];
for( int i = 0; i < room->numLights; ++i ) {
xDist = x - room->light[i].x;
yDist = y - room->light[i].y;
zDist = z - room->light[i].z;
falloff1 = room->light[i].fallOff1;
falloff2 = room->light[i].fallOff2;
intensity1 = room->light[i].intensity1;
intensity2 = room->light[i].intensity2;
distance = (SQR(xDist) + SQR(yDist) + SQR(zDist)) >> 12;
falloff1 = SQR(falloff1) >> 12;
falloff2 = SQR(falloff2) >> 12;
shade1 = falloff1 * intensity1 / (falloff1 + distance);
shade2 = falloff2 * intensity2 / (falloff2 + distance);
shade = shade1 + (shade2 - shade1) * lightShade / (WIBBLE_SIZE-1);
if( shade > brightest ) {
brightest = shade;
xBrightest = xDist;
yBrightest = yDist;
zBrightest = zDist;
}
}
} else {
for( int i = 0; i < room->numLights; ++i ) {
xDist = x - room->light[i].x;
yDist = y - room->light[i].y;
zDist = z - room->light[i].z;
falloff = room->light[i].fallOff1;
intensity = room->light[i].intensity1;
falloff = SQR(falloff) >> 12;
distance = (SQR(xDist) + SQR(yDist) + SQR(zDist)) >> 12;
shade = falloff * intensity / (falloff + distance);
if( shade > brightest ) {
brightest = shade;
xBrightest = xDist;
yBrightest = yDist;
zBrightest = zDist;
}
}
}
adder = brightest;
// Dynamic light calculation
for( DWORD i = 0; i < DynamicLightCount; ++i ) {
xDist = x - DynamicLights[i].x;
yDist = y - DynamicLights[i].y;
zDist = z - DynamicLights[i].z;
falloff = DynamicLights[i].fallOff1;
intensity = DynamicLights[i].intensity1;
radius = 1 << falloff;
if( (xDist >= -radius && xDist <= radius) &&
(yDist >= -radius && yDist <= radius) &&
(zDist >= -radius && zDist <= radius) )
{
distance = SQR(xDist) + SQR(yDist) + SQR(zDist);
if( distance <= SQR(radius) ) {
shade = (1 << intensity) - (distance >> (2 * falloff - intensity));
if( shade > brightest ) {
brightest = shade;
xBrightest = xDist;
yBrightest = yDist;
zBrightest = zDist;
}
adder += shade;
}
}
}
// Light finalization
adder = adder/2;
if( adder == 0 ) {
LsAdder = room->ambient1;
LsDivider = 0;
} else {
LsAdder = room->ambient1 - adder;
LsDivider = (1 << (W2V_SHIFT + 12)) / adder;
phd_GetVectorAngles(xBrightest, yBrightest, zBrightest, &angles);
phd_RotateLight(angles.pitch, angles.yaw);
}
// Fog calculation
depth = PhdMatrixPtr->_23 >> W2V_SHIFT;
#ifdef FEATURE_VIEW_IMPROVED
LsAdder += CalculateFogShade(depth);
#else // !FEATURE_VIEW_IMPROVED
if( depth > DEPTHQ_START ) // fog begin
LsAdder += depth - DEPTHQ_START;
#endif // FEATURE_VIEW_IMPROVED
if( LsAdder > 0x1FFF ) // fog end
LsAdder = 0x1FFF;
}
void __cdecl S_CalculateStaticLight(__int16 adder) {
int depth;
LsAdder = adder - 0x1000;
depth = PhdMatrixPtr->_23 >> W2V_SHIFT;
#ifdef FEATURE_VIEW_IMPROVED
LsAdder += CalculateFogShade(depth);
#else // !FEATURE_VIEW_IMPROVED
if( depth > DEPTHQ_START ) // fog begin
LsAdder += depth - DEPTHQ_START;
#endif // FEATURE_VIEW_IMPROVED
if( LsAdder > 0x1FFF ) // fog end
LsAdder = 0x1FFF;
}
void __cdecl S_CalculateStaticMeshLight(int x, int y, int z, int shade1, int shade2, ROOM_INFO *room) {
int adder, shade, falloff, intensity;
int xDist, yDist, zDist, distance, radius;
adder = shade1;
if( room->lightMode != 0 ) {
adder += (shade2 - shade1) * RoomLightShades[room->lightMode] / (WIBBLE_SIZE-1);
}
for( DWORD i = 0; i < DynamicLightCount; ++i ) {
xDist = x - DynamicLights[i].x;
yDist = y - DynamicLights[i].y;
zDist = z - DynamicLights[i].z;
falloff = DynamicLights[i].fallOff1;
intensity = DynamicLights[i].intensity1;
radius = 1 << falloff;
if( (xDist >= -radius && xDist <= radius) &&
(yDist >= -radius && yDist <= radius) &&
(zDist >= -radius && zDist <= radius) )
{
distance = SQR(xDist) + SQR(yDist) + SQR(zDist);
if( distance <= SQR(radius) ) {
shade = (1 << intensity) - (distance >> (2 * falloff - intensity));
adder -= shade;
if( adder < 0 ) {
adder = 0;
break;
}
}
}
}
S_CalculateStaticLight(adder);
}
void __cdecl S_LightRoom(ROOM_INFO *room) {
int shade, falloff, intensity;
int xPos, yPos, zPos;
int xDist, yDist, zDist, distance, radius;
int roomVtxCount;
ROOM_VERTEX_INFO *roomVtx;
if( room->lightMode != 0 ) {
int *roomLightTable = RoomLightTables[RoomLightShades[room->lightMode]].table;
roomVtxCount = *room->data;
roomVtx = (ROOM_VERTEX_INFO *)(room->data + 1);
for( int i = 0; i < roomVtxCount; ++i ) {
__int16 wibble = roomLightTable[roomVtx[i].lightTableValue % WIBBLE_SIZE];
roomVtx[i].lightAdder = roomVtx[i].lightBase + wibble;
}
}
else if( (room->flags & 0x10) != 0 ) {
roomVtxCount = *room->data;
roomVtx = (ROOM_VERTEX_INFO *)(room->data + 1);
for( int i = 0; i < roomVtxCount; ++i ) {
roomVtx[i].lightAdder = roomVtx[i].lightBase;
}
room->flags &= ~0x10;
}
int xMin = 0x400;
int zMin = 0x400;
int xMax = 0x400 * (room->ySize - 1);
int zMax = 0x400 * (room->xSize - 1);
for( DWORD i = 0; i < DynamicLightCount; ++i ) {
xPos = DynamicLights[i].x - room->x;
yPos = DynamicLights[i].y;
zPos = DynamicLights[i].z - room->z;
falloff = DynamicLights[i].fallOff1;
intensity = DynamicLights[i].intensity1;
radius = 1 << falloff;
if( xPos + radius >= xMin && zPos + radius >= zMin && xPos - radius <= xMax && zPos - radius <= zMax ) {
room->flags |= 0x10;
roomVtxCount = *room->data;
roomVtx = (ROOM_VERTEX_INFO *)(room->data + 1);
for( int j = 0; j < roomVtxCount; ++j ) {
if( roomVtx[j].lightAdder != 0 ) {
xDist = roomVtx[j].x - xPos;
yDist = roomVtx[j].y - yPos;
zDist = roomVtx[j].z - zPos;
if( (xDist >= -radius && xDist <= radius) &&
(yDist >= -radius && yDist <= radius) &&
(zDist >= -radius && zDist <= radius) )
{
distance = SQR(xDist) + SQR(yDist) + SQR(zDist);
if( distance <= SQR(radius) ) {
shade = (1 << intensity) - (distance >> (2 * falloff - intensity));
roomVtx[j].lightAdder -= shade;
if( roomVtx[j].lightAdder < 0 )
roomVtx[j].lightAdder = 0;
}
}
}
}
}
}
}
void __cdecl S_DrawHealthBar(int percent) {
#ifdef FEATURE_HUD_IMPROVED
int barWidth = GetRenderScale(100);
int barHeight = GetRenderScale(5);
int barXOffset = GetRenderScale((PsxBarPosEnabled && !IsInventoryActive ) ? 20 : 8);
int barYOffset = GetRenderScale((PsxBarPosEnabled && !IsInventoryActive ) ? 18 : 8);
int pixel = GetRenderScale(1);
int x0, x1;
if( PsxBarPosEnabled ) {
x1 = PhdWinMinX + DumpWidth - barXOffset;
x0 = x1 - barWidth;
} else {
x0 = PhdWinMinX + barXOffset;
x1 = x0 + barWidth;
}
int y0 = PhdWinMinY + barYOffset;
int y1 = y0 + barHeight;
int bar = barWidth * percent / PHD_ONE;
// Disable underwater shading
IsShadeEffect = false;
if( HealthBarMode != 0 && SavedAppSettings.RenderMode == RM_Hardware ) {
if( SavedAppSettings.ZBuffer ) {
PSX_DrawHealthBar(x0, y0, x1, y1, bar, pixel, 255);
} else {
PSX_InsertHealthBar(x0, y0, x1, y1, bar, pixel, 255);
}
return;
}
// Frame
ins_flat_rect(x0-pixel*2, y0-pixel*2, x1+pixel*2, y1+pixel*2, PhdNearZ + 50, InvColours[ICLR_White]);
ins_flat_rect(x0-pixel*1, y0-pixel*1, x1+pixel*2, y1+pixel*2, PhdNearZ + 40, InvColours[ICLR_Gray]);
ins_flat_rect(x0-pixel*1, y0-pixel*1, x1+pixel*1, y1+pixel*1, PhdNearZ + 30, InvColours[ICLR_Black]);
// Health bar
if( bar > 0 ) {
ins_flat_rect(x0, y0+pixel*0, x0+bar, y0+barHeight, PhdNearZ + 20, InvColours[ICLR_Red]);
ins_flat_rect(x0, y0+pixel*1, x0+bar, y0+pixel*2, PhdNearZ + 10, InvColours[ICLR_Orange]);
}
#else // !FEATURE_HUD_IMPROVED
int i;
int barWidth = 100;
int barHeight = 5;
int x0 = 8;
int y0 = 8;
int x1 = x0 + barWidth;
int y1 = y0 + barHeight;
int bar = barWidth * percent / 100;
// Disable underwater shading
IsShadeEffect = false;
// Black background
for( i = 0; i < (barHeight+2); ++i )
ins_line(x0-2, y0+i-1, x1+1, y0+i-1, PhdNearZ + 50, InvColours[ICLR_Black]);
// Dark frame
ins_line(x0-2, y1+1, x1+2, y1+1, PhdNearZ + 40, InvColours[ICLR_Gray]);
ins_line(x1+2, y0-2, x1+2, y1+1, PhdNearZ + 40, InvColours[ICLR_Gray]);
// Light frame
ins_line(x0-2, y0-2, x1+2, y0-2, PhdNearZ + 30, InvColours[ICLR_White]);
ins_line(x0-2, y1+1, x0-2, y0-2, PhdNearZ + 30, InvColours[ICLR_White]);
// Health bar
if( bar > 0 ) {
for( i = 0; i < barHeight; ++i )
ins_line(x0, y0+i, x0+bar, y0+i, PhdNearZ + 20, ( i == 1 ) ? InvColours[ICLR_Orange] : InvColours[ICLR_Red]);
}
#endif // FEATURE_HUD_IMPROVED
}
void __cdecl S_DrawAirBar(int percent) {
#ifdef FEATURE_HUD_IMPROVED
int barWidth = GetRenderScale(100);
int barHeight = GetRenderScale(5);
int barXOffset = GetRenderScale(PsxBarPosEnabled ? 20 : 8);
int barYOffset = GetRenderScale(PsxBarPosEnabled ? 32 : 8);
int pixel = GetRenderScale(1);
int x1 = PhdWinMinX + DumpWidth - barXOffset;
int x0 = x1 - barWidth;
int y0 = PhdWinMinY + barYOffset;
int y1 = y0 + barHeight;
int bar = barWidth * percent / PHD_ONE;
// Disable underwater shading
IsShadeEffect = false;
if( HealthBarMode != 0 && SavedAppSettings.RenderMode == RM_Hardware ) {
if( SavedAppSettings.ZBuffer ) {
PSX_DrawAirBar(x0, y0, x1, y1, bar, pixel, 255);
} else {
PSX_InsertAirBar(x0, y0, x1, y1, bar, pixel, 255);
}
return;
}
// Frame
ins_flat_rect(x0-pixel*2, y0-pixel*2, x1+pixel*2, y1+pixel*2, PhdNearZ + 50, InvColours[ICLR_White]);
ins_flat_rect(x0-pixel*1, y0-pixel*1, x1+pixel*2, y1+pixel*2, PhdNearZ + 40, InvColours[ICLR_Gray]);
ins_flat_rect(x0-pixel*1, y0-pixel*1, x1+pixel*1, y1+pixel*1, PhdNearZ + 30, InvColours[ICLR_Black]);
// Air bar
if( bar > 0 ) {
ins_flat_rect(x0, y0+pixel*0, x0+bar, y0+barHeight, PhdNearZ + 20, InvColours[ICLR_Blue]);
ins_flat_rect(x0, y0+pixel*1, x0+bar, y0+pixel*2, PhdNearZ + 10, InvColours[ICLR_White]);
}
#else // !FEATURE_HUD_IMPROVED
int i;
int barWidth = 100;
int barHeight = 5;
int x1 = DumpWidth - 10;
int x0 = x1 - barWidth;
int y0 = 8;
int y1 = y0 + barHeight;
int bar = barWidth * percent / 100;
// Disable underwater shading
IsShadeEffect = false;
// Black background
for( i = 0; i < (barHeight+2); ++i )
ins_line(x0-2, y0+i-1, x1+1, y0+i-1, PhdNearZ + 50, InvColours[ICLR_Black]);
// Dark frame
ins_line(x0-2, y1+1, x1+2, y1+1, PhdNearZ + 40, InvColours[ICLR_Gray]);
ins_line(x1+2, y0-2, x1+2, y1+1, PhdNearZ + 40, InvColours[ICLR_Gray]);
// Light frame
ins_line(x0-2, y0-2, x1+2, y0-2, PhdNearZ + 30, InvColours[ICLR_White]);
ins_line(x0-2, y1+1, x0-2, y0-2, PhdNearZ + 30, InvColours[ICLR_White]);
// Air bar
if( bar > 0 ) {
for( i = 0; i < barHeight; ++i )
ins_line(x0, y0+i, x0+bar, y0+i, PhdNearZ + 20, ( i == 1 ) ? InvColours[ICLR_White] : InvColours[ICLR_Blue]);
}
#endif // FEATURE_HUD_IMPROVED
}
void __cdecl AnimateTextures(int nTicks) {
static int tickComp = 0;
__int16 i, j;
__int16 *ptr;
PHD_TEXTURE temp1, temp2;
tickComp += nTicks;
while( tickComp > TICKS_PER_FRAME * 5 ) {
ptr = AnimatedTextureRanges;
i = *(ptr++);
for( ; i>0; --i, ++ptr ) {
j = *(ptr++);
temp1 = PhdTextureInfo[*ptr];
temp2 = TextureBackupUV[*ptr];
for ( ; j>0; --j, ++ptr ) {
PhdTextureInfo[ptr[0]] = PhdTextureInfo[ptr[1]];
TextureBackupUV[ptr[0]] = TextureBackupUV[ptr[1]];
}
PhdTextureInfo[*ptr] = temp1;
TextureBackupUV[*ptr] = temp2;
}
tickComp -= TICKS_PER_FRAME * 5;
}
}
void __cdecl S_SetupBelowWater(BOOL underwater) {
if( IsWet != underwater ) {
FadeToPal(1, underwater ? WaterPalette : GamePalette8);
IsWet = underwater;
}
IsWaterEffect = true;
IsShadeEffect = true;
IsWibbleEffect = !underwater;
}
void __cdecl S_SetupAboveWater(BOOL underwater) {
IsWaterEffect = false;
IsShadeEffect = underwater;
IsWibbleEffect = underwater;
}
void __cdecl S_AnimateTextures(int nTicks) {
WibbleOffset = (WibbleOffset + nTicks / TICKS_PER_FRAME) % WIBBLE_SIZE;
RoomLightShades[1] = GetRandomDraw() & (WIBBLE_SIZE-1);
RoomLightShades[2] = (WIBBLE_SIZE-1) * (phd_sin(WibbleOffset * PHD_360 / WIBBLE_SIZE) + PHD_IONE) / 2 / PHD_IONE;
if( GF_SunsetEnabled ) {
// NOTE: in the original game there was: SunsetTimer += nTicks;
// so the timer was reset every time when the saved game is loaded
SunsetTimer = SaveGame.statistics.timer * TICKS_PER_FRAME;
CLAMPG(SunsetTimer, SUNSET_TIMEOUT);
RoomLightShades[3] = (WIBBLE_SIZE-1) * SunsetTimer / SUNSET_TIMEOUT;
}
AnimateTextures(nTicks);
}
void __cdecl S_DisplayPicture(LPCTSTR fileName, BOOL isTitle) {
#ifdef FEATURE_BACKGROUND_IMPROVED
if( !isTitle ) {
init_game_malloc();
}
BGND2_LoadPicture(fileName, isTitle, FALSE);
#else // !FEATURE_BACKGROUND_IMPROVED
DWORD bytesRead;
HANDLE hFile;
DWORD fileSize;
DWORD bitmapSize;
BYTE *fileData;
BYTE *bitmapData;
LPCTSTR fullPath;
fullPath = GetFullPath(fileName);
hFile = CreateFile(fullPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE )
return;
if( !isTitle )
init_game_malloc();
fileSize = GetFileSize(hFile, NULL);
fileData = (BYTE *)game_malloc(fileSize, GBUF_LoadPiccyBuffer);
ReadFile(hFile, fileData, fileSize, &bytesRead, NULL);
CloseHandle(hFile);
bitmapSize = 640*480;
bitmapData = (BYTE *)game_malloc(bitmapSize, GBUF_LoadPiccyBuffer);
DecompPCX(fileData, fileSize, bitmapData, PicPalette);
if( SavedAppSettings.RenderMode == RM_Software ) {
#if (DIRECT3D_VERSION >= 0x900)
if( PictureBuffer.bitmap != NULL)
memcpy(PictureBuffer.bitmap, bitmapData, PictureBuffer.width * PictureBuffer.height);
#else // (DIRECT3D_VERSION >= 0x900)
WinVidCopyBitmapToBuffer(PictureBufferSurface, bitmapData);
#endif // (DIRECT3D_VERSION >= 0x900)
} else {
BGND_Make640x480(bitmapData, PicPalette);
}
if( !isTitle ) {
#if (DIRECT3D_VERSION >= 0x900)
memcpy(GamePalette8, PicPalette, sizeof(GamePalette8));
#else // (DIRECT3D_VERSION >= 0x900)
CopyBitmapPalette(PicPalette, bitmapData, bitmapSize, GamePalette8);
#endif // (DIRECT3D_VERSION >= 0x900)
}
game_free(fileSize + bitmapSize);
#endif // FEATURE_BACKGROUND_IMPROVED
}
void __cdecl S_SyncPictureBufferPalette() {
#if (DIRECT3D_VERSION >= 0x900)
if( PictureBuffer.bitmap == NULL ) return;
SyncSurfacePalettes(PictureBuffer.bitmap, PictureBuffer.width, PictureBuffer.height, PictureBuffer.width, PicPalette, PictureBuffer.bitmap, PictureBuffer.width, GamePalette8, TRUE);
memcpy(PicPalette, GamePalette8, sizeof(PicPalette));
#else // (DIRECT3D_VERSION >= 0x900)
DDSDESC desc;
#ifdef FEATURE_BACKGROUND_IMPROVED
int width = BGND_PictureWidth;
int height = BGND_PictureHeight;
#else // !FEATURE_BACKGROUND_IMPROVED
int width = 640;
int height = 480;
#endif // FEATURE_BACKGROUND_IMPROVED
if( PictureBufferSurface == NULL || FAILED(WinVidBufferLock(PictureBufferSurface, &desc, DDLOCK_WRITEONLY|DDLOCK_WAIT)) )
return;
SyncSurfacePalettes(desc.lpSurface, width, height, desc.lPitch, PicPalette, desc.lpSurface, desc.lPitch, GamePalette8, TRUE);
WinVidBufferUnlock(PictureBufferSurface, &desc);
memcpy(PicPalette, GamePalette8, sizeof(PicPalette));
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __cdecl S_DontDisplayPicture() {
if( SavedAppSettings.RenderMode == RM_Hardware ) {
BGND_Free();
BGND_PictureIsReady = false;
}
}
void __cdecl ScreenDump() {
UpdateFrame(true, NULL);
}
void __cdecl ScreenPartialDump() {
UpdateFrame(true, &PhdWinRect);
}
void __cdecl FadeToPal(int fadeValue, RGB888 *palette) {
int i, j;
int palStartIdx = 0;
int palEndIdx = 256;
#if (DIRECT3D_VERSION < 0x900)
int palSize = 256;
#endif // (DIRECT3D_VERSION < 0x900)
PALETTEENTRY fadePal[256];
#if (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.RenderMode != RM_Software )
return;
#else // (DIRECT3D_VERSION >= 0x900)
if( !GameVid_IsVga )
return;
if( GameVid_IsWindowedVga ) {
palStartIdx += 10;
palEndIdx -= 10;
palSize -= 20;
}
#endif // (DIRECT3D_VERSION >= 0x900)
if( fadeValue <= 1 ) {
for( i=palStartIdx; i= 0x900)
S_InitialisePolyList(FALSE);
S_OutputPolyList();
#else // (DIRECT3D_VERSION >= 0x900)
DDrawPalette->SetEntries(0, palStartIdx, palSize, &WinVidPalette[palStartIdx]);
#endif // (DIRECT3D_VERSION >= 0x900)
return;
}
for( i=palStartIdx; i= 0x900)
S_InitialisePolyList(FALSE);
S_OutputPolyList();
#else // (DIRECT3D_VERSION >= 0x900)
DDrawPalette->SetEntries(0, palStartIdx, palSize, &WinVidPalette[palStartIdx]);
#endif // (DIRECT3D_VERSION >= 0x900)
S_DumpScreen();
}
}
void __cdecl ScreenClear(bool isPhdWinSize) {
DWORD flags = ( SavedAppSettings.RenderMode == RM_Hardware ) ? CLRB_BackBuffer : CLRB_RenderBuffer;
if( isPhdWinSize )
flags |= CLRB_PhdWinSize;
ClearBuffers(flags, 0);
}
void __cdecl S_CopyScreenToBuffer() {
#if (DIRECT3D_VERSION < 0x900)
DDSDESC desc;
#endif // (DIRECT3D_VERSION < 0x900)
#ifdef FEATURE_BACKGROUND_IMPROVED
DWORD bgndMode = 0;
if( IsFadeToBlack ) {
bgndMode = 0;
} else if( IsInventoryActive ) {
bgndMode = InvBackgroundMode;
} else if( GF_CurrentEvent() == GFE_LEVCOMPLETE ) {
bgndMode = StatsBackgroundMode;
}
DWORD width = PhdWinWidth;
DWORD height = PhdWinHeight;
#else // !FEATURE_BACKGROUND_IMPROVED
DWORD width = 640;
DWORD height = 480;
#endif // FEATURE_BACKGROUND_IMPROVED
if( SavedAppSettings.RenderMode == RM_Software ) {
#ifdef FEATURE_BACKGROUND_IMPROVED
#if (DIRECT3D_VERSION >= 0x900)
if( PictureBuffer.bitmap == NULL ||
PictureBuffer.width != width ||
PictureBuffer.height != height )
{
BGND_PictureWidth = width;
BGND_PictureHeight = height;
try {
CreatePictureBuffer();
} catch(...) {
return;
}
}
#else // (DIRECT3D_VERSION >= 0x900)
if( PictureBufferSurface != NULL &&
(BGND_PictureWidth != width || BGND_PictureHeight != height) )
{
PictureBufferSurface->Release();
PictureBufferSurface = NULL;
}
if( PictureBufferSurface == NULL ) {
BGND_PictureWidth = width;
BGND_PictureHeight = height;
try {
CreatePictureBuffer();
} catch(...) {
return;
}
}
#endif // (DIRECT3D_VERSION >= 0x900)
#endif // FEATURE_BACKGROUND_IMPROVED
#if (DIRECT3D_VERSION >= 0x900)
SWR_StretchBlt(&PictureBuffer, NULL, &RenderBuffer, &GameVidRect);
#else // (DIRECT3D_VERSION >= 0x900)
PictureBufferSurface->Blt(NULL, RenderBufferSurface, &GameVidRect, DDBLT_WAIT, NULL);
#endif // (DIRECT3D_VERSION >= 0x900)
#if (DIRECT3D_VERSION >= 0x900)
#ifdef FEATURE_BACKGROUND_IMPROVED
if( InventoryMode != INV_PauseMode || PauseBackgroundMode != 0 )
#endif // FEATURE_BACKGROUND_IMPROVED
{
BYTE *ptr = PictureBuffer.bitmap;
DWORD num = PictureBuffer.width * PictureBuffer.height;
for( DWORD i = 0; i < num; ++i ) {
ptr[i] = DepthQIndex[ptr[i]];
}
}
#else // (DIRECT3D_VERSION >= 0x900)
if(
#ifdef FEATURE_BACKGROUND_IMPROVED
(InventoryMode != INV_PauseMode || PauseBackgroundMode != 0) &&
#endif // FEATURE_BACKGROUND_IMPROVED
SUCCEEDED(WinVidBufferLock(PictureBufferSurface, &desc, DDLOCK_WRITEONLY|DDLOCK_WAIT)) )
{
BYTE *surface = (BYTE *)desc.lpSurface;
for( DWORD i = 0; i < height; ++i ) {
for( DWORD j = 0; j < width; ++j ) {
surface[j] = DepthQIndex[surface[j]];
}
surface += desc.lPitch;
}
WinVidBufferUnlock(PictureBufferSurface, &desc);
}
#endif // (DIRECT3D_VERSION >= 0x900)
memcpy(PicPalette, GamePalette8, sizeof(PicPalette));
}
#ifdef FEATURE_BACKGROUND_IMPROVED
else if( bgndMode == 0 ) {
BGND2_CapturePicture();
}
#endif // FEATURE_BACKGROUND_IMPROVED
}
void __cdecl S_CopyBufferToScreen() {
#if defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
DWORD color = SavedAppSettings.LightingMode ? 0xFF808080 : 0xFFFFFFFF;
#else // defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
DWORD color = 0xFFFFFFFF; // vertex color (ARGB white)
#endif // defined(FEATURE_VIDEOFX_IMPROVED) && (DIRECT3D_VERSION >= 0x900)
#ifdef FEATURE_BACKGROUND_IMPROVED
DWORD bgndMode = 0;
if( IsFadeToBlack ) {
bgndMode = 0;
} else if( IsInventoryActive ) {
bgndMode = InvBackgroundMode;
} else if( GF_CurrentEvent() == GFE_LEVCOMPLETE ) {
bgndMode = StatsBackgroundMode;
}
#endif // FEATURE_BACKGROUND_IMPROVED
if( SavedAppSettings.RenderMode == RM_Software ) {
#if (DIRECT3D_VERSION >= 0x900)
if( PictureBuffer.bitmap == NULL ) {
return;
}
#else // (DIRECT3D_VERSION >= 0x900)
if( PictureBufferSurface == NULL ) { // NOTE: additional check just in case
return;
}
#endif // (DIRECT3D_VERSION >= 0x900)
if( memcmp(GamePalette8, PicPalette, sizeof(PicPalette)) ) {
S_SyncPictureBufferPalette();
}
#ifdef FEATURE_BACKGROUND_IMPROVED
RECT rect = PhdWinRect;
BGND2_CalculatePictureRect(&rect);
#if (DIRECT3D_VERSION >= 0x900)
ClearBuffers(CLRB_RenderBuffer, 0);
SWR_StretchBlt(&RenderBuffer, &rect, &PictureBuffer, NULL);
#else // (DIRECT3D_VERSION >= 0x900)
RenderBufferSurface->Blt(&rect, PictureBufferSurface, NULL, DDBLT_WAIT, NULL);
#endif // (DIRECT3D_VERSION >= 0x900)
}
else if( BGND_PictureIsReady && (!BGND_IsCaptured || bgndMode == 0) ) {
HWR_EnableZBuffer(false, false);
RECT rect = PhdWinRect;
if( !BGND_IsCaptured ) {
BGND2_LoadPicture(NULL, FALSE, TRUE); // reload picture if required
}
BGND_DrawInGameBlack(); // draw black background for picture margins
BGND2_CalculatePictureRect(&rect);
BGND2_DrawTextures(&rect, color);
if( BGND_IsCaptured ) {
if( InventoryMode != INV_PauseMode ) {
BGND2_FadeTo(128, -12); // the captured background image fades out to 50%
} else if( PauseBackgroundMode != 0 ) {
BGND2_FadeTo(128, -128); // the captured background image instantly gets 50%
}
}
#else // !FEATURE_BACKGROUND_IMPROVED
#if (DIRECT3D_VERSION >= 0x900)
SWR_StretchBlt(&RenderBuffer, &GameVidRect, &PictureBuffer, NULL);
#else // (DIRECT3D_VERSION >= 0x900)
RenderBufferSurface->Blt(&GameVidRect, PictureBufferSurface, NULL, DDBLT_WAIT, NULL);
#endif // (DIRECT3D_VERSION >= 0x900)
}
else if( BGND_PictureIsReady ) {
BGND_GetPageHandles();
HWR_EnableZBuffer(false, false);
static const int tileX[4] = {0, 256, 512, 640};
static const int tileY[3] = {0, 256, 480};
int i, x[4], y[3];
for( i = 0; i < 4; ++i )
x[i] = tileX[i] * PhdWinWidth / 640 + PhdWinMinX;
for( i = 0; i < 3; ++i )
y[i] = tileY[i] * PhdWinHeight / 480 + PhdWinMinY;
DrawTextureTile(x[0], y[0], x[1]-x[0], y[1]-y[0], BGND_PageHandles[0],
0, 0, 256, 256, color, color, color, color);
DrawTextureTile(x[1], y[0], x[2]-x[1], y[1]-y[0], BGND_PageHandles[1],
0, 0, 256, 256, color, color, color, color);
DrawTextureTile(x[2], y[0], x[3]-x[2], y[1]-y[0], BGND_PageHandles[2],
0, 0, 128, 256, color, color, color, color);
DrawTextureTile(x[0], y[1], x[1]-x[0], y[2]-y[1], BGND_PageHandles[3],
0, 0, 256, 224, color, color, color, color);
DrawTextureTile(x[1], y[1], x[2]-x[1], y[2]-y[1], BGND_PageHandles[4],
0, 0, 256, 224, color, color, color, color);
DrawTextureTile(x[2], y[1], x[3]-x[2], y[2]-y[1], BGND_PageHandles[2],
128, 0, 128, 224, color, color, color, color);
#endif // FEATURE_BACKGROUND_IMPROVED
HWR_EnableZBuffer(true, true);
}
else {
BGND_DrawInGameBackground();
}
}
BOOL __cdecl DecompPCX(LPCBYTE pcx, DWORD pcxSize, LPBYTE pic, RGB888 *pal) {
PCX_HEADER *header;
DWORD w, h, width, height, pitch;
LPCBYTE src;
LPBYTE dst;
header = (PCX_HEADER *)pcx;
width = header->xMax - header->xMin + 1;
height = header->yMax - header->yMin + 1;
if( header->manufacturer != 10 ||
header->version < 5 ||
header->bpp != 8 ||
header->rle != 1 ||
header->planes != 1 ||
width*height == 0 )
{
return FALSE;
}
src = pcx + sizeof(PCX_HEADER);
dst = pic;
pitch = width + width%2; // add padding if required
h = 0;
w = 0;
// NOTE: PCX decoder slightly redesigned to be more compatible and stable
while( h < height ) {
if( (*src & 0xC0) == 0xC0 ) {
BYTE n = (*src++) & 0x3F;
BYTE c = *src++;
if( n > 0 ) {
if( w < width ) {
CLAMPG(n, width - w);
memset(dst, c, n);
dst += n;
}
w += n;
}
} else {
*dst++ = *src++;
++w;
}
if( w >= pitch ) {
w = 0;
++h;
}
}
if( pal != NULL)
memcpy(pal, pcx + pcxSize - sizeof(RGB888)*256, sizeof(RGB888)*256);
return TRUE;
}
// NOTE: this function is not presented in the original game
int GetPcxResolution(LPCBYTE pcx, DWORD pcxSize, DWORD *width, DWORD *height) {
PCX_HEADER *header;
if( pcx == NULL || pcxSize <= sizeof(PCX_HEADER) || width == NULL || height == NULL ) {
return -1;
}
header = (PCX_HEADER *)pcx;
*width = header->xMax - header->xMin + 1;
*height = header->yMax - header->yMin + 1;
if( header->manufacturer != 10 ||
header->version < 5 ||
header->bpp != 8 ||
header->rle != 1 ||
header->planes != 1 ||
*width == 0 ||
*height == 0 )
{
return -1;
}
return 0;
}
/*
* Inject function
*/
void Inject_Output() {
INJECT(0x00450BA0, GetRenderHeight);
INJECT(0x00450BB0, GetRenderWidth);
INJECT(0x00450BC0, S_InitialisePolyList);
INJECT(0x00450CB0, S_DumpScreen);
INJECT(0x00450CF0, S_ClearScreen);
INJECT(0x00450D00, S_InitialiseScreen);
INJECT(0x00450D40, S_OutputPolyList);
INJECT(0x00450D80, S_GetObjectBounds);
INJECT(0x00450FF0, S_InsertBackPolygon);
INJECT(0x00451040, S_PrintShadow);
INJECT(0x00451240, S_CalculateLight);
INJECT(0x00451540, S_CalculateStaticLight);
INJECT(0x00451580, S_CalculateStaticMeshLight);
INJECT(0x004516B0, S_LightRoom);
INJECT(0x004518C0, S_DrawHealthBar);
INJECT(0x00451A90, S_DrawAirBar);
INJECT(0x00451C90, AnimateTextures);
INJECT(0x00451D50, S_SetupBelowWater);
INJECT(0x00451DB0, S_SetupAboveWater);
INJECT(0x00451DE0, S_AnimateTextures);
INJECT(0x00451EA0, S_DisplayPicture);
INJECT(0x00451FB0, S_SyncPictureBufferPalette);
INJECT(0x00452030, S_DontDisplayPicture);
INJECT(0x00452040, ScreenDump);
INJECT(0x00452050, ScreenPartialDump);
INJECT(0x00452060, FadeToPal);
INJECT(0x00452230, ScreenClear);
INJECT(0x00452260, S_CopyScreenToBuffer);
INJECT(0x00452310, S_CopyBufferToScreen);
INJECT(0x00452360, DecompPCX);
}
================================================
FILE: specific/output.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef OUTPUT_H_INCLUDED
#define OUTPUT_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
int __cdecl GetRenderScale(int unit);
int __cdecl GetRenderHeightDownscaled();
int __cdecl GetRenderWidthDownscaled();
int __cdecl GetRenderHeight(); // 0x00450BA0
int __cdecl GetRenderWidth(); // 0x00450BB0
void __cdecl S_InitialisePolyList(BOOL clearBackBuffer); // 0x00450BC0
DWORD __cdecl S_DumpScreen(); // 0x00450CB0
void __cdecl S_ClearScreen(); // 0x00450CF0
void __cdecl S_InitialiseScreen(GF_LEVEL_TYPE levelType); // 0x00450D00
void __cdecl S_OutputPolyList(); // 0x00450D40
int __cdecl S_GetObjectBounds(__int16 *bPtr); // 0x00450D80
void __cdecl S_InsertBackPolygon(int x0, int y0, int x1, int y1); // 0x00450FF0
void __cdecl S_PrintShadow(__int16 radius, __int16 *bPtr, ITEM_INFO *item); // 0x00451040
void __cdecl S_CalculateLight(int x, int y, int z, __int16 roomNumber); // 0x00451240
void __cdecl S_CalculateStaticLight(__int16 adder); // 0x00451540
void __cdecl S_CalculateStaticMeshLight(int x, int y, int z, int shade1, int shade2, ROOM_INFO *room); // 0x00451580
void __cdecl S_LightRoom(ROOM_INFO *room); // 0x004516B0
void __cdecl S_DrawHealthBar(int percent); // 0x004518C0
void __cdecl S_DrawAirBar(int percent); // 0x00451A90
void __cdecl AnimateTextures(int nTicks); // 0x00451C90
void __cdecl S_SetupBelowWater(BOOL underwater); // 0x00451D50
void __cdecl S_SetupAboveWater(BOOL underwater); // 0x00451DB0
void __cdecl S_AnimateTextures(int nTicks); // 0x00451DE0
void __cdecl S_DisplayPicture(LPCTSTR fileName, BOOL reallocGame); // 0x00451EA0
void __cdecl S_SyncPictureBufferPalette(); // 0x00451FB0
void __cdecl S_DontDisplayPicture(); // 0x00452030
void __cdecl ScreenDump(); // 0x00452040
void __cdecl ScreenPartialDump(); // 0x00452050
void __cdecl FadeToPal(int fadeValue, RGB888 *palette); // 0x00452060
void __cdecl ScreenClear(bool isPhdWinSize); // 0x00452230
void __cdecl S_CopyScreenToBuffer(); // 0x00452260
void __cdecl S_CopyBufferToScreen(); // 0x00452310
BOOL __cdecl DecompPCX(LPCBYTE pcx, DWORD pcxSize, LPBYTE pic, RGB888 *pal); // 0x00452360
// NOTE: this function is not presented in the original game
int GetPcxResolution(LPCBYTE pcx, DWORD pcxSize, DWORD *width, DWORD *height);
#endif // OUTPUT_H_INCLUDED
================================================
FILE: specific/registry.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/registry.h"
#include "global/vars.h"
static const BYTE GuidMask[] = { '{',3,2,1,0,'-',5,4,'-',7,6,'-',8,9,10,11,12,13,14,15,'}' };
static const char HexChars[] = "0123456789ABCDEF";
static inline BYTE HexChar(char c) {
if( c>='0' && c<='9' )
return c-'0';
if( c>='A' && c<='F' )
return c-'A'+10;
if( c>='a' && c<='f' )
return c-'a'+10;
return 0xFF;
}
LPCTSTR __cdecl GuidBinaryToString(GUID *guid) {
static char guidStringBuffer[GUID_STRING_SIZE];
LPTSTR strPtr = guidStringBuffer;
for( DWORD i=0; i= 16 ) {
*(strPtr++) = GuidMask[i];
} else {
BYTE code = ((BYTE *)guid)[GuidMask[i]];
*(strPtr++) = HexChars[code >> 4];
*(strPtr++) = HexChars[code & 0xF];
}
}
*strPtr = 0;
return guidStringBuffer;
}
bool __cdecl GuidStringToBinary(LPCTSTR lpString, GUID *guid) {
for( DWORD i=0; i= 16 ) {
if( *(lpString++) != GuidMask[i] )
return false;
} else {
BYTE hex1 = HexChar(*(lpString++));
BYTE hex2 = HexChar(*(lpString++));
if( hex1 >= 16 || hex2 >= 16 )
return false;
((BYTE *)guid)[GuidMask[i]] = (hex1 << 4)|(hex2 & 0xF);
}
}
return ( *lpString == 0 ); // check if the string ends with zero
}
BOOL __cdecl OpenRegistryKey(LPCTSTR lpSubKey) {
return ( ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, lpSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &HKey, &RegKeyDisposition) );
}
bool __cdecl IsNewRegistryKeyCreated() {
return ( RegKeyDisposition == REG_CREATED_NEW_KEY );
}
LONG __cdecl CloseRegistryKey() {
return RegCloseKey(HKey);
}
LONG __cdecl SetRegistryDwordValue(LPCTSTR lpValueName, DWORD value) {
return RegSetValueEx(HKey, lpValueName, 0, REG_DWORD, (const BYTE *)&value, sizeof(DWORD));
}
LONG __cdecl SetRegistryBoolValue(LPCTSTR lpValueName, bool value) {
DWORD dwValue = value;
return RegSetValueEx(HKey, lpValueName, 0, REG_DWORD, (const BYTE *)&dwValue, sizeof(DWORD));
}
LONG __cdecl SetRegistryFloatValue(LPCTSTR lpValueName, double value) {
int stringLen;
char stringBuf[64];
stringLen = sprintf(stringBuf, "%.5f", value);
return SetRegistryStringValue(lpValueName, stringBuf, stringLen);
}
LONG __cdecl SetRegistryBinaryValue(LPCTSTR lpValueName, LPBYTE value, DWORD valueSize) {
if( value != NULL )
return RegSetValueEx(HKey, lpValueName, 0, REG_BINARY, value, valueSize);
else
return RegDeleteValue(HKey, lpValueName);
}
LONG __cdecl SetRegistryStringValue(LPCTSTR lpValueName, LPCTSTR value, int length) {
if( value == NULL )
return RegDeleteValue(HKey, lpValueName);
if( length < 0 )
length = lstrlen(value);
return RegSetValueEx(HKey, lpValueName, 0, REG_SZ, (const BYTE *)value, length+1);
}
LONG __cdecl DeleteRegistryValue(LPCTSTR lpValueName) {
return RegDeleteValue(HKey, lpValueName);
}
bool __cdecl GetRegistryDwordValue(LPCTSTR lpValueName, DWORD *pValue, DWORD defaultValue) {
DWORD dwType;
DWORD dwSize = sizeof(DWORD);
if( ERROR_SUCCESS == RegQueryValueEx(HKey, lpValueName, 0, &dwType, (LPBYTE)pValue, &dwSize) &&
dwType == REG_DWORD && dwSize == sizeof(DWORD) )
{
return true;
}
SetRegistryDwordValue(lpValueName, defaultValue);
*pValue = defaultValue;
return false;
}
bool __cdecl GetRegistryBoolValue(LPCTSTR lpValueName, bool *pValue, bool defaultValue) {
DWORD dwType, dwValue;
DWORD dwSize = sizeof(DWORD);
if( ERROR_SUCCESS == RegQueryValueEx(HKey, lpValueName, 0, &dwType, (LPBYTE)&dwValue, &dwSize) &&
dwType == REG_DWORD && dwSize == sizeof(DWORD) )
{
*pValue = dwValue ? true : false;
return true;
}
SetRegistryBoolValue(lpValueName, defaultValue);
*pValue = defaultValue;
return false;
}
bool __cdecl GetRegistryFloatValue(LPCTSTR lpValueName, double *value, double defaultValue) {
char stringBuf[64];
if( GetRegistryStringValue(lpValueName, stringBuf, sizeof(stringBuf), NULL) ) {
*value = atof(stringBuf);
return true;
}
SetRegistryFloatValue(lpValueName, defaultValue);
*value = defaultValue;
return false;
}
bool __cdecl GetRegistryBinaryValue(LPCTSTR lpValueName, LPBYTE value, DWORD valueSize, LPBYTE defaultValue) {
DWORD dwType;
DWORD dwSize = valueSize;
if( ERROR_SUCCESS == RegQueryValueEx(HKey, lpValueName, 0, &dwType, value, &dwSize) &&
dwType == REG_BINARY && dwSize == valueSize )
{
return true;
}
if( defaultValue != NULL )
SetRegistryBinaryValue(lpValueName, defaultValue, valueSize);
else
RegDeleteValue(HKey, lpValueName);
return false;
}
bool __cdecl GetRegistryStringValue(LPCTSTR lpValueName, LPTSTR value, DWORD maxSize, LPCTSTR defaultValue) {
DWORD dwType;
DWORD dwSize = maxSize;
if( ERROR_SUCCESS == RegQueryValueEx(HKey, lpValueName, 0, &dwType, (LPBYTE)value, &dwSize) &&
dwType == REG_SZ )
{
return true;
}
if( defaultValue != NULL ) {
SetRegistryStringValue(lpValueName, defaultValue, -1);
dwSize = lstrlen(defaultValue) + 1;
if( dwSize > maxSize ) {
dwSize = maxSize-1;
value[dwSize] = 0;
}
memcpy(value, defaultValue, dwSize);
} else {
RegDeleteValue(HKey, lpValueName);
}
return false;
}
bool __cdecl GetRegistryGuidValue(LPCTSTR lpValueName, GUID *value, GUID *defaultValue) {
LPCTSTR guidString;
char guidStringBuffer[GUID_STRING_SIZE];
if( GetRegistryStringValue(lpValueName, guidStringBuffer, GUID_STRING_SIZE, NULL) &&
GuidStringToBinary(guidStringBuffer, value) )
{
return true;
}
if( defaultValue != NULL ) {
guidString = GuidBinaryToString(defaultValue);
SetRegistryStringValue(lpValueName, guidString, GUID_STRING_SIZE-1);
*value = *defaultValue;
} else {
RegDeleteValue(HKey, lpValueName);
}
return false;
}
/*
* Inject function
*/
void Inject_Registry() {
INJECT(0x00456A20, GuidBinaryToString);
INJECT(0x00456A80, GuidStringToBinary);
INJECT(0x00456B30, OpenRegistryKey);
INJECT(0x00456B60, IsNewRegistryKeyCreated);
INJECT(0x00456B70, CloseRegistryKey);
INJECT(0x00456B80, SetRegistryDwordValue);
INJECT(0x00456BA0, SetRegistryBoolValue);
INJECT(0x00456BD0, SetRegistryFloatValue);
INJECT(0x00456C10, SetRegistryBinaryValue);
INJECT(0x00456C50, SetRegistryStringValue);
INJECT(0x00456CA0, DeleteRegistryValue);
INJECT(0x00456CC0, GetRegistryDwordValue);
INJECT(0x00456D20, GetRegistryBoolValue);
INJECT(0x00456DA0, GetRegistryFloatValue);
INJECT(0x00456E00, GetRegistryBinaryValue);
INJECT(0x00456E80, GetRegistryStringValue);
INJECT(0x00456F20, GetRegistryGuidValue);
}
================================================
FILE: specific/registry.h
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef REGISTRY_H_INCLUDED
#define REGISTRY_H_INCLUDED
#include "global/types.h"
// Registry path
#define REG_TR2_PATH "Software\\Core Design\\Tomb Raider II"
// Registry keys
#define REG_SYSTEM_KEY "System"
#define REG_GAME_KEY "Game"
#define REG_VIEW_KEY "View"
#define REG_BUGS_KEY "Bugs"
// DWORD value names
#define REG_DISPLAY_ADAPTER "PreferredDisplayAdapterGUID"
#define REG_SOUND_ADAPTER "PreferredSoundAdapterGUID"
#define REG_JOYSTICK "PreferredJoystickGUID"
#define REG_RENDERER "RenderMode"
#define REG_FS_WIDTH "FullScreenWidth"
#define REG_FS_HEIGHT "FullScreenHeight"
#define REG_FS_BPP "FullScreenBPP"
#define REG_WIN_WIDTH "WindowWidth"
#define REG_WIN_HEIGHT "WindowHeight"
#define REG_WIN_ASPECT "AspectMode"
#define REG_ADJUST_MODE "TexelAdjustMode"
#define REG_ADJUST_NEAREST "NearestAdjustment"
#define REG_ADJUST_LINEAR "LinearAdjustment"
#define REG_LIGHTING_MODE "LightingMode"
#define REG_MUSIC_VOLUME "MusicVolume"
#define REG_SOUND_VOLUME "SoundFXVolume"
#define REG_DETAIL_LEVEL "DetailLevel"
#define REG_INVBGND_MODE "InvBackgroundMode"
#define REG_STATSBGND_MODE "StatsBackgroundMode"
#define REG_PICTURE_STRETCH "PictureStretchLimit"
#define REG_LOADING_SCREENS "LoadingScreens"
#define REG_CUSTOM_WATER_COLOR "CustomWaterColor"
#define REG_SHADOW_MODE "ShadowMode"
#define REG_REFLECTION_MODE "ReflectionMode"
#define REG_ALPHABLEND_MODE "AlphaBlendMode"
#define REG_INVTEXTBOX_MODE "InvTextBoxMode"
#define REG_HEALTHBAR_MODE "HealthBarMode"
#define REG_PICKUPITEM_MODE "PickupItemMode"
#define REG_DEMOTEXT_MODE "DemoTextMode"
#define REG_SAVEGAME_SLOTS "SavegameSlots"
#define REG_SCREENSHOT_FORMAT "ScreenshotFormat"
#define REG_JOYSTICK_BTN_STYLE "JoystickButtonStyle"
#define REG_PAUSEBGND_MODE "PauseBackgroundMode"
// BOOL value names
#define REG_PERSPECTIVE "PerspectiveCorrect"
#define REG_DITHER "Dither"
#define REG_ZBUFFER "ZBuffer"
#define REG_BILINEAR "BilinearFiltering"
#define REG_TRIPLEBUFFER "TripleBuffering"
#define REG_FULLSCREEN "FullScreen"
#define REG_SOUND_ENABLE "SoundEnabled"
#define REG_LARA_MIC "LaraMic"
#define REG_JOY_ENABLE "JoystickEnabled"
#define REG_16BIT_DISABLE "Disable16BitTextures"
#define REG_SORT_DISABLE "DontSortPrimitives"
#define REG_FLIP_BROKEN "FlipBroken"
#define REG_FMV_DISABLE "DisableFMV"
#define REG_PSXBARPOS_ENABLE "EnablePsxBarPos"
#define REG_PSXFOV_ENABLE "EnablePsxFov"
#define REG_BAREFOOT_SFX_ENABLE "BarefootSFX"
#define REG_REMASTER_PIX_ENABLE "RemasteredPictures"
#define REG_WALK_TO_SIDESTEP "WalkToSidestep"
#define REG_JOYSTICK_VIBRATION "JoystickVibration"
#define REG_JOYSTICK_LED_COLOR "JoystickLedColor"
#define REG_JOYSTICK_HINTS "JoystickHints"
#define REG_AVOID_INTERLACED "AvoidInterlacedVideoModes"
#define REG_RUNNING_M16_FIX "RunningM16fix"
#define REG_LOWCEILING_JUMP_FIX "LowCeilingJumpFix"
// FLOAT value names
#define REG_GAME_SIZER "Sizer"
#define REG_INV_MUSIC_MUTE "InvMusicMute"
#define REG_UW_MUSIC_MUTE "UwMusicMute"
#define REG_DRAW_DISTANCE "DrawDistance"
#define REG_FOG_BEGIN "FogBegin"
#define REG_FOG_END "FogEnd"
#define REG_UW_FOG_BEGIN "UwFogBegin"
#define REG_UW_FOG_END "UwFogEnd"
#define REG_GAME_GUI_SCALE "GameGUIScale"
#define REG_INV_GUI_SCALE "InvGUIScale"
// BINARY value names
#define REG_GAME_LAYOUT "Layout"
#define REG_GAME_KBD_LAYOUT "KeyboardLayout"
#define REG_GAME_JOY_LAYOUT "JoystickLayout"
#define REG_GAME_ASSAULT "Assault"
// STRING value names
#define REG_SCREENSHOT_PATH "ScreenshotPath"
#define REG_PICTURE_SUFFIX "PictureSuffix"
// GUID string size
#define GUID_STRING_SIZE (sizeof("{00112233-4455-6677-8899AABBCCDDEEFF}"))
/*
* Function list
*/
LPCTSTR __cdecl GuidBinaryToString(GUID *guid); // 0x00456A20
bool __cdecl GuidStringToBinary(LPCTSTR lpString, GUID *guid); // 0x00456A80
BOOL __cdecl OpenRegistryKey(LPCTSTR lpSubKey); // 0x00456B30
bool __cdecl IsNewRegistryKeyCreated(); // 0x00456B60
LONG __cdecl CloseRegistryKey(); // 0x00456B70
LONG __cdecl SetRegistryDwordValue(LPCTSTR lpValueName, DWORD value); // 0x00456B80
LONG __cdecl SetRegistryBoolValue(LPCTSTR lpValueName, bool value); // 0x00456BA0
LONG __cdecl SetRegistryFloatValue(LPCTSTR lpValueName, double value); // 0x00456BD0
LONG __cdecl SetRegistryBinaryValue(LPCTSTR lpValueName, LPBYTE value, DWORD valueSize); // 0x00456C10
LONG __cdecl SetRegistryStringValue(LPCTSTR lpValueName, LPCTSTR value, int length); // 0x00456C50
LONG __cdecl DeleteRegistryValue(LPCTSTR lpValueName); // 0x00456CA0
bool __cdecl GetRegistryDwordValue(LPCTSTR lpValueName, DWORD *pValue, DWORD defaultValue); // 0x00456CC0
bool __cdecl GetRegistryBoolValue(LPCTSTR lpValueName, bool *pValue, bool defaultValue); // 0x00456D20
bool __cdecl GetRegistryFloatValue(LPCTSTR lpValueName, double *value, double defaultValue); // 0x00456DA0
bool __cdecl GetRegistryBinaryValue(LPCTSTR lpValueName, LPBYTE value, DWORD valueSize, LPBYTE defaultValue); // 0x00456E00
bool __cdecl GetRegistryStringValue(LPCTSTR lpValueName, LPTSTR value, DWORD maxSize, LPCTSTR defaultValue); // 0x00456E80
bool __cdecl GetRegistryGuidValue(LPCTSTR lpValueName, GUID *value, GUID *defaultValue); // 0x00456F20
#endif // REGISTRY_H_INCLUDED
================================================
FILE: specific/screenshot.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/screenshot.h"
#include "specific/winvid.h"
#include "global/vars.h"
#ifdef FEATURE_SCREENSHOT_IMPROVED
#include "modding/file_utils.h"
#include "modding/gdi_utils.h"
extern LPDDS CaptureBufferSurface;
DWORD ScreenshotFormat = 1;
char ScreenshotPath[MAX_PATH];
typedef struct {
HBITMAP bitmap;
GDI_FILEFMT format;
BYTE quality;
char fileName[MAX_PATH];
} TASK_PARAMS;
static DWORD WINAPI SaveImageTask(CONST LPVOID lpParam) {
TASK_PARAMS *params = (TASK_PARAMS *)lpParam;
if( params != NULL ) {
if( params->bitmap != NULL ) {
GDI_SaveImageFile(params->fileName, params->format, params->quality, params->bitmap);
DeleteObject(params->bitmap);
}
delete params;
}
ExitThread(0);
}
static void __cdecl ScreenShotPNG(LPDDS screen) {
static SYSTEMTIME lastTime = {0, 0, 0, 0, 0, 0, 0, 0};
static int lastIndex = 0;
RECT rect = {0, 0, 0, 0};
HDC dc;
#if (DIRECT3D_VERSION >= 0x900)
screen = NULL;
DISPLAY_MODE mode;
if( CaptureBufferSurface != NULL ) {
screen = CaptureBufferSurface;
} else if( !WinVidGetDisplayMode(&mode)
|| FAILED(D3DDev->CreateOffscreenPlainSurface(mode.width, mode.height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &screen, NULL))
|| FAILED(D3DDev->GetFrontBufferData(0, screen)) )
{
if( screen != NULL ) screen->Release();
return;
}
if FAILED(screen->GetDC(&dc)) {
if( screen != CaptureBufferSurface ) {
screen->Release();
}
return;
}
#else // (DIRECT3D_VERSION >= 0x900)
if( screen == NULL || FAILED(screen->GetDC(&dc)) ) {
return;
}
#endif // (DIRECT3D_VERSION >= 0x900)
if( GetClientRect(HGameWindow, &rect) ) {
HBITMAP bitmap;
LPVOID lpBits;
if( CaptureBufferSurface == NULL ) {
MapWindowPoints(HGameWindow, GetParent(HGameWindow), (LPPOINT)&rect, 2);
}
bitmap = CreateBitmapFromDC(dc, &rect, &lpBits, WinVidPalette);
if( bitmap != NULL ) {
TASK_PARAMS *params = new TASK_PARAMS;
params->bitmap = bitmap;
params->format = GDI_PNG;
params->quality = 100;
CreateDateTimeFilename(params->fileName, sizeof(params->fileName), ScreenshotPath, ".png", &lastTime, &lastIndex);
CreateDirectories(params->fileName, true);
if( !CreateThread(NULL, 0, &SaveImageTask, params, 0, NULL) ) {
// if failed to create a thread, we just save the image
GDI_SaveImageFile(params->fileName, params->format, params->quality, params->bitmap);
DeleteObject(bitmap);
delete params;
}
}
}
screen->ReleaseDC(dc);
#if (DIRECT3D_VERSION >= 0x900)
if( screen != CaptureBufferSurface ) {
screen->Release();
}
#endif // (DIRECT3D_VERSION >= 0x900)
}
#endif // FEATURE_SCREENSHOT_IMPROVED
static TGA_HEADER ScreenShotTgaHeader = {
0, 0,
.dataTypeCode = 2, // Uncompressed, RGB images
0, 0, 0, 0, 0,
.width = 320,
.height = 256,
.bpp = 16,
.imageDescriptor = 0,
};
// NOTE: This function is not presented in the original code
// but the code is taken away form ScreenShot() and extended
// to be compatible with 24/32 bit
static void __cdecl ScreenShotTGA(LPDDS screen, BYTE tgaBpp) {
static int scrshotNumber = 0;
DWORD i, j;
BYTE *src, *dst;
DDSDESC desc;
BYTE *tgaPic = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD bytesWritten;
#if defined(FEATURE_SCREENSHOT_IMPROVED)
char fileName[MAX_PATH];
#else // !FEATURE_SCREENSHOT_IMPROVED
char fileName[128];
#endif // FEATURE_SCREENSHOT_IMPROVED
DWORD width = 0;
DWORD height = 0;
#if (DIRECT3D_VERSION >= 0x900)
if( tgaBpp != 24 )
return;
#else // (DIRECT3D_VERSION >= 0x900)
if( tgaBpp != 16 && tgaBpp != 24 )
return;
#endif // (DIRECT3D_VERSION >= 0x900)
memset(&desc, 0, sizeof(desc));
#if (DIRECT3D_VERSION < 0x900)
desc.dwSize = sizeof(desc);
#endif // (DIRECT3D_VERSION < 0x900)
#if defined(FEATURE_SCREENSHOT_IMPROVED)
RECT rect = {0,0,0,0};
// do game window screenshot, not the whole screen
if( GetClientRect(HGameWindow, &rect) ) {
if( CaptureBufferSurface == NULL ) {
MapWindowPoints(HGameWindow, GetParent(HGameWindow), (LPPOINT)&rect, 2);
}
width = ABS(rect.right - rect.left);
height = ABS(rect.bottom - rect.top);
}
#if (DIRECT3D_VERSION >= 0x900)
screen = NULL;
DISPLAY_MODE mode;
if( CaptureBufferSurface != NULL ) {
screen = CaptureBufferSurface;
} else if( !WinVidGetDisplayMode(&mode)
|| FAILED(D3DDev->CreateOffscreenPlainSurface(mode.width, mode.height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &screen, NULL))
|| FAILED(D3DDev->GetFrontBufferData(0, screen)) )
{
goto CLEANUP;
}
if FAILED(screen->LockRect(&desc, &rect, D3DLOCK_READONLY)) {
goto CLEANUP;
}
#else // (DIRECT3D_VERSION >= 0x900)
HRESULT rc;
do {
rc = screen->Lock(&rect, &desc, DDLOCK_READONLY|DDLOCK_WAIT, NULL);
} while( rc == DDERR_WASSTILLDRAWING );
if( rc == DDERR_SURFACELOST )
rc = screen->Restore();
if FAILED(rc)
return;
if( width == 0 || width > desc.dwWidth )
width = desc.dwWidth;
if( height == 0 || height > desc.dwHeight )
height = desc.dwHeight;
#endif // (DIRECT3D_VERSION >= 0x900)
scrshotNumber = CreateSequenceFilename(fileName, sizeof(fileName), ScreenshotPath, ".tga", "tomb", 4, scrshotNumber);
if( scrshotNumber < 0 ) goto CLEANUP;
++scrshotNumber;
CreateDirectories(fileName, true); // create whole path just in case if it's not created yet
#else // !FEATURE_SCREENSHOT_IMPROVED
#if (DIRECT3D_VERSION < 0x900)
if FAILED(WinVidBufferLock(screen, &desc, DDLOCK_WRITEONLY|DDLOCK_WAIT))
return;
width = desc.dwWidth;
height = desc.dwHeight;
#endif // (DIRECT3D_VERSION < 0x900)
wsprintf(fileName, "tomb%04d.tga", scrshotNumber++);
#endif // FEATURE_SCREENSHOT_IMPROVED
hFile = CreateFile(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile == INVALID_HANDLE_VALUE )
goto CLEANUP;
ScreenShotTgaHeader.width = width;
ScreenShotTgaHeader.height = height;
ScreenShotTgaHeader.bpp = tgaBpp;
WriteFile(hFile, &ScreenShotTgaHeader, sizeof(TGA_HEADER), &bytesWritten, NULL);
#if defined(FEATURE_SCREENSHOT_IMPROVED)
// NOTE: There was unsafe memory usage in the original code. The game just used GameAllocMemPointer buffer!
// No new memory allocations. On higher resolutions there was critical data overwriting and game crashed
tgaPic = (BYTE *)GlobalAlloc(GMEM_FIXED, width*height*(tgaBpp/8));
#else // !FEATURE_SCREENSHOT_IMPROVED
tgaPic = (BYTE *)GameAllocMemPointer;
#endif // FEATURE_SCREENSHOT_IMPROVED
if( tgaPic == NULL )
goto CLEANUP;
// We need to load bitmap lines to TGA starting from the bottom line
#if (DIRECT3D_VERSION >= 0x900)
src = (BYTE *)desc.pBits + desc.Pitch*(height - 1);
#else // (DIRECT3D_VERSION >= 0x900)
#if defined(FEATURE_SCREENSHOT_IMPROVED)
// NOTE: There was bug in the original formula: src = lpSurface + lPitch * dwHeight
// Height must be subtracted by 1 in this formula
src = (BYTE *)desc.lpSurface + desc.lPitch*(height - 1);
#else // !FEATURE_SCREENSHOT_IMPROVED
src = (BYTE *)desc.lpSurface + desc.lPitch*height;
#endif // FEATURE_SCREENSHOT_IMPROVED
#endif // (DIRECT3D_VERSION >= 0x900)
dst = tgaPic;
#if (DIRECT3D_VERSION >= 0x900)
for( i=0; i < height; ++i ) {
for( j=0; j < width; ++j ) {
((RGB888 *)dst)[j] = *(RGB888*)(src + j * 4);
}
src -= desc.Pitch;
dst += sizeof(RGB888)*width;
}
#else // (DIRECT3D_VERSION >= 0x900)
if( tgaBpp == 16 ) {
for( i=0; i < height; ++i ) {
// R5G6B5 - not TGA compatible
if( desc.ddpfPixelFormat.dwRBitMask == 0xF800 ) {
// right shift highest 10 bits (R+G) over lowest G bit
for( j=0; j < width; ++j ) {
UINT16 sample = ((UINT16 *)src)[j];
((UINT16 *)dst)[j] = ((sample & 0xFFC0) >> 1) | (sample & 0x001F);
}
} else {
// X1R5G5B5 - already TGA compatible
memcpy(dst, src, sizeof(UINT16)*width);
}
src -= desc.lPitch;
dst += sizeof(UINT16)*width;
}
} else {
for( i=0; i < height; ++i ) {
if( desc.ddpfPixelFormat.dwRGBBitCount == 24 ) {
memcpy(dst, src, sizeof(RGB888)*width);
} else {
for( j=0; j < width; ++j ) {
((RGB888 *)dst)[j] = *(RGB888*)(src + j * (desc.ddpfPixelFormat.dwRGBBitCount / 8));
}
}
src -= desc.lPitch;
dst += sizeof(RGB888)*width;
}
}
#endif // (DIRECT3D_VERSION >= 0x900)
WriteFile(hFile, tgaPic, width*height*(tgaBpp/8), &bytesWritten, NULL);
CLEANUP :
#if defined(FEATURE_SCREENSHOT_IMPROVED)
if( tgaPic != NULL )
GlobalFree((HGLOBAL)tgaPic);
#endif // FEATURE_SCREENSHOT_IMPROVED
if( hFile != INVALID_HANDLE_VALUE )
CloseHandle(hFile);
#if (DIRECT3D_VERSION >= 0x900)
if( screen != NULL ) {
screen->UnlockRect();
if( screen != CaptureBufferSurface ) {
screen->Release();
}
}
#else // (DIRECT3D_VERSION >= 0x900)
#if defined(FEATURE_SCREENSHOT_IMPROVED)
screen->Unlock(desc.lpSurface);
#else // !FEATURE_SCREENSHOT_IMPROVED
WinVidBufferUnlock(screen, &desc);
#endif // FEATURE_SCREENSHOT_IMPROVED
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __cdecl ScreenShotPCX() {
static int scrshotNumber = 0;
#if (DIRECT3D_VERSION < 0x900)
HRESULT rc;
LPDDS screen;
DDSDESC desc;
#endif // (DIRECT3D_VERSION < 0x900)
BYTE *pcxData = NULL;
DWORD pcxSize;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD bytesWritten;
#if defined(FEATURE_SCREENSHOT_IMPROVED)
char fileName[MAX_PATH];
#else // !FEATURE_SCREENSHOT_IMPROVED
char fileName[128];
#endif // FEATURE_SCREENSHOT_IMPROVED
#if (DIRECT3D_VERSION >= 0x900)
if( !RenderBuffer.bitmap || !RenderBuffer.width || !RenderBuffer.height ) return;
pcxSize = CompPCX(RenderBuffer.bitmap, RenderBuffer.width, RenderBuffer.height, GamePalette8, &pcxData);
#else // (DIRECT3D_VERSION >= 0x900)
screen = ( SavedAppSettings.RenderMode == RM_Software ) ? RenderBufferSurface : PrimaryBufferSurface;
desc.dwSize = sizeof(desc);
do {
rc = screen->Lock(NULL, &desc, DDLOCK_SURFACEMEMORYPTR, NULL);
} while( rc == DDERR_WASSTILLDRAWING );
if( rc == DDERR_SURFACELOST )
rc = screen->Restore();
if FAILED(rc)
return;
pcxSize = CompPCX((BYTE *)desc.lpSurface, desc.dwWidth, desc.dwHeight, GamePalette8, &pcxData);
screen->Unlock(&desc);
#endif // (DIRECT3D_VERSION >= 0x900)
if( pcxSize == 0 || pcxData == NULL )
return;
#if defined(FEATURE_SCREENSHOT_IMPROVED)
scrshotNumber = CreateSequenceFilename(fileName, sizeof(fileName), ScreenshotPath, ".pcx", "tomb", 4, scrshotNumber);
if( scrshotNumber < 0 ) return;
++scrshotNumber;
CreateDirectories(fileName, true); // create whole path just in case if it is not created yet
#else // !FEATURE_SCREENSHOT_IMPROVED
if( ++scrshotNumber > 9999 ) scrshotNumber = 1;
wsprintf(fileName, "tomb%04d.pcx", scrshotNumber);
#endif // FEATURE_SCREENSHOT_IMPROVED
hFile = CreateFile(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if( hFile != INVALID_HANDLE_VALUE ) {
WriteFile(hFile, pcxData, pcxSize, &bytesWritten, NULL);
CloseHandle(hFile);
}
GlobalFree(pcxData);
}
DWORD __cdecl CompPCX(BYTE *bitmap, DWORD width, DWORD height, RGB888 *palette, BYTE **pcxData) {
DWORD i;
PCX_HEADER *pcxHeader;
BYTE *picData;
*pcxData = (BYTE *)GlobalAlloc(GMEM_FIXED, width*height*2 + sizeof(PCX_HEADER) + sizeof(RGB888)*256);
if( *pcxData == NULL )
return 0;
pcxHeader = *(PCX_HEADER **)pcxData;
pcxHeader->manufacturer = 10;
pcxHeader->version = 5;
pcxHeader->rle = 1;
pcxHeader->bpp = 8;
pcxHeader->planes = 1;
pcxHeader->xMin = 0;
pcxHeader->yMin = 0;
pcxHeader->xMax = width - 1;
pcxHeader->yMax = height - 1;
pcxHeader->h_dpi = width;
pcxHeader->v_dpi = height;
pcxHeader->bytesPerLine = width;
picData = *pcxData + sizeof(PCX_HEADER);
for( i=0; i 63 ) {
return 0;
}
if( num == 1 && (value & 0xC0) != 0xC0 ) {
buffer[0] = value;
return 1;
}
buffer[0] = num | 0xC0;
buffer[1] = value;
return 2;
}
void __cdecl ScreenShot(LPDDS screen) {
#if defined(FEATURE_SCREENSHOT_IMPROVED)
#if (DIRECT3D_VERSION < 0x900)
if( SavedAppSettings.RenderMode == RM_Software ) {
screen = RenderBufferSurface;
} else if( CaptureBufferSurface != NULL ) {
screen = CaptureBufferSurface;
}
#endif // (DIRECT3D_VERSION < 0x900)
if( ScreenshotFormat > 0 ) {
ScreenShotPNG(screen);
return;
}
#endif // FEATURE_SCREENSHOT_IMPROVED
#if (DIRECT3D_VERSION >= 0x900)
if( SavedAppSettings.RenderMode == RM_Software ) {
ScreenShotPCX();
} else {
ScreenShotTGA(screen, 24);
}
#else // (DIRECT3D_VERSION >= 0x900)
DDSDESC desc;
memset(&desc, 0, sizeof(desc));
desc.dwSize = sizeof(desc);
if SUCCEEDED( screen->GetSurfaceDesc(&desc)) {
switch( desc.ddpfPixelFormat.dwRGBBitCount ) {
case 8 :
ScreenShotPCX();
break;
case 16 :
ScreenShotTGA(screen, 16);
break;
#if defined(FEATURE_SCREENSHOT_IMPROVED)
case 24 :
case 32 :
// NOTE: the original game cannot make 24/32 bit screenshots
ScreenShotTGA(screen, 24);
break;
#endif // FEATURE_SCREENSHOT_IMPROVED
default :
break;
}
}
#endif // (DIRECT3D_VERSION >= 0x900)
}
/*
* Inject function
*/
void Inject_Screenshot() {
INJECT(0x0044E9A0, ScreenShotPCX);
INJECT(0x0044EAB0, CompPCX);
INJECT(0x0044EB60, EncodeLinePCX);
INJECT(0x0044EC40, EncodePutPCX);
INJECT(0x0044EC80, ScreenShot);
}
================================================
FILE: specific/screenshot.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef SCREENSHOT_H_INCLUDED
#define SCREENSHOT_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __cdecl ScreenShotPCX(); // 0x0044E9A0
DWORD __cdecl CompPCX(BYTE *bitmap, DWORD width, DWORD height, RGB888 *palette, BYTE **pcxData); // 0x0044EAB0
DWORD __cdecl EncodeLinePCX(BYTE *src, DWORD width, BYTE *dst); // 0x0044EB60
DWORD __cdecl EncodePutPCX(BYTE value, BYTE num, BYTE *buffer); // 0x0044EC40
void __cdecl ScreenShot(LPDDS screen); // 0x0044EC80
#endif // SCREENSHOT_H_INCLUDED
================================================
FILE: specific/setupdlg.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/setupdlg.h"
#include "specific/init_display.h"
#include "specific/init_input.h"
#include "specific/init_sound.h"
#include "specific/registry.h"
#include "specific/setupwnd.h"
#include "specific/utils.h"
#include "specific/winvid.h"
#include "global/resource.h"
#include "global/vars.h"
#include
#if defined(FEATURE_NOLEGACY_OPTIONS) || defined(FEATURE_VIDEOFX_IMPROVED)
APP_SETTINGS ChangedAppSettings, SavedAppSettings;
#endif // defined(FEATURE_NOLEGACY_OPTIONS) || defined(FEATURE_VIDEOFX_IMPROVED)
static LPCTSTR String_Software3d = "Software (256 Colors)";
static LPCTSTR String_Hardware3d = "Hardware 3D Acceleration";
static LPCTSTR String_FullScreen = "Full Screen";
static LPCTSTR String_Windowed = "Windowed";
static LPCTSTR String_ZBuffered = "Z Buffered";
static LPCTSTR String_BilinearFiltered = "Bilinear Filtered";
#ifndef FEATURE_NOLEGACY_OPTIONS
static LPCTSTR String_Dithered = "Dithered";
static LPCTSTR String_TripleBuffered = "Triple Buffered";
static LPCTSTR String_PerspectiveCorrect = "Perspective Correct";
#endif // FEATURE_NOLEGACY_OPTIONS
static LPCTSTR String_None = "None";
static LPCTSTR String_NA = "n/a";
static LPCTSTR String_Enabled = "Enabled";
static LPCTSTR String_Disabled = "Disabled";
static LPCTSTR String_Lara = "Lara";
static LPCTSTR String_Camera = "Camera";
static LPCTSTR Strings_Aspect[] = {" (4:3)", " (16:9)", ""};
#ifndef FEATURE_NOLEGACY_OPTIONS
static LPCTSTR String_HighColor = "High Color";
static LPCTSTR String_TrueColor = "True Color";
static LPCTSTR String_256Color = "256 Color";
static LPCTSTR String_ModeX = "Mode X";
static LPCTSTR String_StandardVga = "Standard VGA";
#endif // FEATURE_NOLEGACY_OPTIONS
static LPCTSTR String_VidTestPassed = "PASSED";
static LPCTSTR String_VidTestFailed = "FAILED";
static LPCTSTR String_VidSettingsOK = "Current Settings OK";
bool __cdecl OpenGameRegistryKey(LPCTSTR key) {
if( key == NULL )
return OpenRegistryKey(REG_TR2_PATH);
char buf[256];
wsprintf(buf, "%s\\%s", REG_TR2_PATH, key);
return OpenRegistryKey(buf);
}
LONG __cdecl CloseGameRegistryKey() {
return CloseRegistryKey();
}
bool __cdecl SE_WriteAppSettings(APP_SETTINGS *settings) {
LPCTSTR guidString;
if( !OpenGameRegistryKey(REG_SYSTEM_KEY) )
return false;
if( settings->PreferredDisplayAdapter != NULL && settings->PreferredDisplayAdapter != PrimaryDisplayAdapter ) {
guidString = GuidBinaryToString(&settings->PreferredDisplayAdapter->body.adapterGuid);
SetRegistryStringValue(REG_DISPLAY_ADAPTER, guidString, GUID_STRING_SIZE-1);
} else {
DeleteRegistryValue(REG_DISPLAY_ADAPTER);
}
if( settings->PreferredSoundAdapter != NULL && settings->PreferredSoundAdapter != PrimarySoundAdapter ) {
guidString = GuidBinaryToString(&settings->PreferredSoundAdapter->body.adapterGuid);
SetRegistryStringValue(REG_SOUND_ADAPTER, guidString, GUID_STRING_SIZE-1);
} else {
DeleteRegistryValue(REG_SOUND_ADAPTER);
}
if( settings->PreferredJoystick != NULL ) {
guidString = GuidBinaryToString(&settings->PreferredJoystick->body.joystickGuid);
SetRegistryStringValue(REG_JOYSTICK, guidString, GUID_STRING_SIZE-1);
} else {
DeleteRegistryValue(REG_JOYSTICK);
}
SetRegistryDwordValue(REG_RENDERER, settings->RenderMode);
if( settings->PreferredDisplayAdapter != NULL && settings->VideoMode != NULL ) {
SetRegistryDwordValue(REG_FS_WIDTH, settings->VideoMode->body.width);
SetRegistryDwordValue(REG_FS_HEIGHT, settings->VideoMode->body.height);
SetRegistryDwordValue(REG_FS_BPP, settings->VideoMode->body.bpp);
} else {
DeleteRegistryValue(REG_FS_WIDTH);
DeleteRegistryValue(REG_FS_HEIGHT);
DeleteRegistryValue(REG_FS_BPP);
}
SetRegistryDwordValue(REG_WIN_WIDTH, settings->WindowWidth);
SetRegistryDwordValue(REG_WIN_HEIGHT, settings->WindowHeight);
SetRegistryDwordValue(REG_WIN_ASPECT, settings->AspectMode);
#ifndef FEATURE_NOLEGACY_OPTIONS
SetRegistryDwordValue(REG_ADJUST_MODE, settings->TexelAdjustMode);
SetRegistryDwordValue(REG_ADJUST_NEAREST, settings->NearestAdjustment);
SetRegistryDwordValue(REG_ADJUST_LINEAR, settings->LinearAdjustment);
SetRegistryBoolValue(REG_PERSPECTIVE, settings->PerspectiveCorrect);
SetRegistryBoolValue(REG_DITHER, settings->Dither);
SetRegistryBoolValue(REG_TRIPLEBUFFER, settings->TripleBuffering);
SetRegistryBoolValue(REG_16BIT_DISABLE, settings->Disable16BitTextures);
SetRegistryBoolValue(REG_SORT_DISABLE, settings->DontSortPrimitives);
#endif // FEATURE_NOLEGACY_OPTIONS
SetRegistryBoolValue(REG_ZBUFFER, settings->ZBuffer);
SetRegistryBoolValue(REG_BILINEAR, settings->BilinearFiltering);
SetRegistryBoolValue(REG_FULLSCREEN, settings->FullScreen);
SetRegistryBoolValue(REG_SOUND_ENABLE, settings->SoundEnabled);
SetRegistryBoolValue(REG_LARA_MIC, settings->LaraMic);
SetRegistryBoolValue(REG_JOY_ENABLE, settings->JoystickEnabled);
SetRegistryBoolValue(REG_FMV_DISABLE, settings->DisableFMV);
#ifdef FEATURE_VIDEOFX_IMPROVED
SetRegistryDwordValue(REG_LIGHTING_MODE, settings->LightingMode);
#endif // FEATURE_VIDEOFX_IMPROVED
CloseGameRegistryKey();
return true;
}
int __cdecl SE_ReadAppSettings(APP_SETTINGS *settings) {
bool rc;
GUID guid;
DISPLAY_MODE targetMode;
DISPLAY_MODE_LIST *modeList;
DISPLAY_MODE_NODE *mode;
if( !OpenGameRegistryKey(REG_SYSTEM_KEY) )
return 0;
rc = GetRegistryGuidValue(REG_DISPLAY_ADAPTER, &guid, NULL);
settings->PreferredDisplayAdapter = WinVidGetDisplayAdapter(rc ? &guid : NULL);
rc = GetRegistryGuidValue(REG_SOUND_ADAPTER, &guid, NULL);
settings->PreferredSoundAdapter = GetSoundAdapter(rc ? &guid : NULL);
rc = GetRegistryGuidValue(REG_JOYSTICK, &guid, NULL);
settings->PreferredJoystick = GetJoystick(rc ? &guid : NULL);
GetRegistryDwordValue(REG_RENDERER, (DWORD *)&settings->RenderMode, RM_Hardware);
if( settings->RenderMode < RM_Software || settings->RenderMode > RM_Hardware )
settings->RenderMode = RM_Hardware;
#ifdef FEATURE_NOLEGACY_OPTIONS
GetRegistryDwordValue(REG_FS_WIDTH, (DWORD *)&targetMode.width, 1920);
GetRegistryDwordValue(REG_FS_HEIGHT, (DWORD *)&targetMode.height, 1080);
GetRegistryDwordValue(REG_FS_BPP, (DWORD *)&targetMode.bpp, 32);
#else // FEATURE_NOLEGACY_OPTIONS
GetRegistryDwordValue(REG_FS_WIDTH, (DWORD *)&targetMode.width, 640);
GetRegistryDwordValue(REG_FS_HEIGHT, (DWORD *)&targetMode.height, 480);
GetRegistryDwordValue(REG_FS_BPP, (DWORD *)&targetMode.bpp, 16);
#endif // FEATURE_NOLEGACY_OPTIONS
targetMode.vga = VGA_NoVga;
if( settings->RenderMode == RM_Software )
targetMode.bpp = 8;
if( settings->RenderMode == RM_Hardware )
modeList = &settings->PreferredDisplayAdapter->body.hwDispModeList;
else
modeList = &settings->PreferredDisplayAdapter->body.swDispModeList;
#ifdef FEATURE_NOLEGACY_OPTIONS
if( modeList->head ) {
targetMode.bpp = modeList->head->body.bpp;
targetMode.vga = modeList->head->body.vga;
}
#endif // FEATURE_NOLEGACY_OPTIONS
for( mode = modeList->head; mode; mode = mode->next ) {
if( !CompareVideoModes(&mode->body, &targetMode) )
break;
}
settings->VideoMode = mode ? mode : modeList->tail;
#ifdef FEATURE_NOLEGACY_OPTIONS
GetRegistryDwordValue(REG_WIN_WIDTH, (DWORD *)&settings->WindowWidth, 1024);
GetRegistryDwordValue(REG_WIN_HEIGHT, (DWORD *)&settings->WindowHeight, 768);
GetRegistryDwordValue(REG_WIN_ASPECT, (DWORD *)&settings->AspectMode, AM_Any);
#else // FEATURE_NOLEGACY_OPTIONS
GetRegistryDwordValue(REG_WIN_WIDTH, (DWORD *)&settings->WindowWidth, 512);
GetRegistryDwordValue(REG_WIN_HEIGHT, (DWORD *)&settings->WindowHeight, 384);
GetRegistryDwordValue(REG_WIN_ASPECT, (DWORD *)&settings->AspectMode, AM_4_3);
#endif // FEATURE_NOLEGACY_OPTIONS
if( settings->AspectMode < AM_4_3 || settings->AspectMode > AM_Any )
settings->AspectMode = AM_4_3;
#ifdef FEATURE_NOLEGACY_OPTIONS
settings->TexelAdjustMode = TAM_BilinearOnly;
settings->NearestAdjustment = 1;
settings->LinearAdjustment = 128;
settings->PerspectiveCorrect = true;
settings->Dither = true;
settings->TripleBuffering = false;
settings->Disable16BitTextures = false;
settings->DontSortPrimitives = false;
settings->FlipBroken = false;
#else // FEATURE_NOLEGACY_OPTIONS
GetRegistryDwordValue(REG_ADJUST_MODE, (DWORD *)&settings->TexelAdjustMode, TAM_Always);
GetRegistryDwordValue(REG_ADJUST_NEAREST, (DWORD *)&settings->NearestAdjustment, 16);
GetRegistryDwordValue(REG_ADJUST_LINEAR, (DWORD *)&settings->LinearAdjustment, 128);
if( settings->TexelAdjustMode < TAM_Disabled || settings->TexelAdjustMode > TAM_Always )
settings->TexelAdjustMode = TAM_Always;
CLAMP(settings->NearestAdjustment, 0, 256);
CLAMP(settings->LinearAdjustment, 0, 256);
GetRegistryBoolValue(REG_PERSPECTIVE, &settings->PerspectiveCorrect, settings->RenderMode == RM_Hardware);
GetRegistryBoolValue(REG_DITHER, &settings->Dither, false);
GetRegistryBoolValue(REG_TRIPLEBUFFER, &settings->TripleBuffering, false);
GetRegistryBoolValue(REG_16BIT_DISABLE, &settings->Disable16BitTextures, false);
GetRegistryBoolValue(REG_SORT_DISABLE, &settings->DontSortPrimitives, false);
GetRegistryBoolValue(REG_FLIP_BROKEN, &settings->FlipBroken, false);
#endif // FEATURE_NOLEGACY_OPTIONS
GetRegistryBoolValue(REG_ZBUFFER, &settings->ZBuffer, true);
GetRegistryBoolValue(REG_BILINEAR, &settings->BilinearFiltering, true);
GetRegistryBoolValue(REG_FULLSCREEN, &settings->FullScreen, true);
GetRegistryBoolValue(REG_SOUND_ENABLE, &settings->SoundEnabled, true);
GetRegistryBoolValue(REG_LARA_MIC, &settings->LaraMic, false);
GetRegistryBoolValue(REG_JOY_ENABLE, &settings->JoystickEnabled, true);
GetRegistryBoolValue(REG_FMV_DISABLE, &settings->DisableFMV, false);
#ifdef FEATURE_VIDEOFX_IMPROVED
GetRegistryDwordValue(REG_LIGHTING_MODE, (DWORD *)&settings->LightingMode, true);
CLAMPG(settings->LightingMode, 2);
#endif // FEATURE_VIDEOFX_IMPROVED
CloseGameRegistryKey();
return IsNewRegistryKeyCreated() ? 2 : 1;
}
bool __cdecl SE_GraphicsTestStart() {
try {
WinVidStart();
RenderStart(true);
} catch(int error) {
LPCTSTR errorString = DecodeErrorMessage(error);
// 'Test Result' Static
SetDlgItemText(GraphicsDialogHandle, ID_GRAPH_STATIC_TESTRESULT, String_VidTestFailed);
// 'Test Result Details' Static
SetDlgItemText(GraphicsDialogHandle, ID_GRAPH_STATIC_TESTDETAIL, errorString);
return false;
}
// 'Test Result' Static
SetDlgItemText(GraphicsDialogHandle, ID_GRAPH_STATIC_TESTRESULT, String_VidTestPassed);
// 'Test Result Details' Static
SetDlgItemText(GraphicsDialogHandle, ID_GRAPH_STATIC_TESTDETAIL, String_VidSettingsOK);
return true;
}
void __cdecl SE_GraphicsTestFinish() {
RenderFinish(true);
WinVidFinish();
WinVidHideGameWindow();
}
int __cdecl SE_GraphicsTestExecute() {
return 0;
}
int __cdecl SE_GraphicsTest() {
int result = 0;
APP_SETTINGS backupAppSettings = SavedAppSettings;
if( SE_GraphicsTestStart() )
result = SE_GraphicsTestExecute();
SE_GraphicsTestFinish();
SavedAppSettings = backupAppSettings;
return result;
}
void __cdecl SE_DefaultGraphicsSettings() {
DISPLAY_ADAPTER_NODE *adapter, *swAdapter, *hwAdapter;
DISPLAY_MODE_LIST *modeList;
DISPLAY_MODE_NODE *mode;
DISPLAY_MODE targetMode;
hwAdapter = NULL;
swAdapter = NULL;
for( adapter = DisplayAdapterList.head; adapter; adapter = adapter->next ) {
if( adapter->body.lpAdapterGuid == NULL )
swAdapter = adapter;
#if (DIRECT3D_VERSION >= 0x900)
hwAdapter = adapter;
ChangedAppSettings.ZBuffer = true;
ChangedAppSettings.BilinearFiltering = true;
#else // (DIRECT3D_VERSION >= 0x900)
if( adapter->body.hwRenderSupported && (hwAdapter == NULL || adapter != swAdapter) )
{
hwAdapter = adapter;
#ifndef FEATURE_NOLEGACY_OPTIONS
ChangedAppSettings.PerspectiveCorrect = adapter->body.perspectiveCorrectSupported;
ChangedAppSettings.Dither = adapter->body.ditherSupported;
#endif // FEATURE_NOLEGACY_OPTIONS
ChangedAppSettings.ZBuffer = adapter->body.zBufferSupported;
ChangedAppSettings.BilinearFiltering = adapter->body.linearFilterSupported;
}
#endif // (DIRECT3D_VERSION >= 0x900)
}
if( hwAdapter != NULL ) {
ChangedAppSettings.PreferredDisplayAdapter = hwAdapter;
ChangedAppSettings.RenderMode = RM_Hardware;
} else {
ChangedAppSettings.PreferredDisplayAdapter = swAdapter;
ChangedAppSettings.RenderMode = RM_Software;
#ifndef FEATURE_NOLEGACY_OPTIONS
ChangedAppSettings.PerspectiveCorrect = true;
#endif // FEATURE_NOLEGACY_OPTIONS
}
ChangedAppSettings.FullScreen = true;
#ifdef FEATURE_NOLEGACY_OPTIONS
ChangedAppSettings.AspectMode = AM_Any;
ChangedAppSettings.WindowWidth = 1024;
ChangedAppSettings.WindowHeight = 768;
#else // FEATURE_NOLEGACY_OPTIONS
ChangedAppSettings.WindowWidth = 512;
ChangedAppSettings.WindowHeight = 384;
#endif // FEATURE_NOLEGACY_OPTIONS
if( ChangedAppSettings.PreferredDisplayAdapter == NULL )
return;
#ifdef FEATURE_NOLEGACY_OPTIONS
targetMode.width = 1920;
targetMode.height = 1080;
targetMode.bpp = 32;
#else // FEATURE_NOLEGACY_OPTIONS
targetMode.width = 640;
targetMode.height = 480;
targetMode.bpp = 16;
#endif // FEATURE_NOLEGACY_OPTIONS
targetMode.vga = VGA_NoVga;
if( ChangedAppSettings.RenderMode == RM_Hardware )
modeList = &ChangedAppSettings.PreferredDisplayAdapter->body.hwDispModeList;
else
modeList = &ChangedAppSettings.PreferredDisplayAdapter->body.swDispModeList;
#ifdef FEATURE_NOLEGACY_OPTIONS
if( modeList->head ) {
targetMode.bpp = modeList->head->body.bpp;
targetMode.vga = modeList->head->body.vga;
}
#endif // FEATURE_NOLEGACY_OPTIONS
for( mode = modeList->head; mode; mode = mode->next ) {
if( !CompareVideoModes(&mode->body, &targetMode) )
break;
}
ChangedAppSettings.VideoMode = mode ? mode : modeList->tail;
}
bool __cdecl SE_SoundTestStart() {
BYTE *waveFile;
WAVEFORMATEX format;
DWORD dataSize;
LPVOID data;
try {
WinSndStart(SoundDialogHandle);
} catch(...) {
return false;
}
waveFile = (BYTE *)UT_LoadResource(MAKEINTRESOURCE(IDR_WAVESAMPLE), "WAVE");
format = *(WAVEFORMATEX *)&waveFile[0x14];
format.cbSize = 0;
dataSize = *(DWORD *)&waveFile[0x28];
data = &waveFile[0x2C];
return WinSndMakeSample(0, &format, data, dataSize);
}
void __cdecl SE_SoundTestFinish() {
WinSndFinish();
}
int __cdecl SE_SoundTestExecute() {
int channel = WinSndPlaySample(0, VOLUME_PCT(90), PHD_ONE, DSBPAN_CENTER, 0);
if( channel >= 0 )
while( WinSndIsChannelPlaying(channel) ) /* just wait */;
return 0;
}
int __cdecl SE_SoundTest() {
int result = 0;
APP_SETTINGS backupAppSettings = SavedAppSettings;
SavedAppSettings = ChangedAppSettings;
if( SE_SoundTestStart() )
result = SE_SoundTestExecute();
SE_SoundTestFinish();
SavedAppSettings = backupAppSettings;
return result;
}
int CALLBACK SE_PropSheetCallback(HWND hwndDlg, UINT uMsg, LPARAM lParam) {
switch( uMsg ) {
case PSCB_PRECREATE :
GraphicsDialogHandle = NULL;
break;
case PSCB_INITIALIZED :
SE_PropSheetWndHandle = hwndDlg;
SE_OldPropSheetWndProc = (WNDPROC)SetWindowLong(hwndDlg, GWL_WNDPROC, (LONG)SE_NewPropSheetWndProc);
break;
}
return 0;
}
LRESULT CALLBACK SE_NewPropSheetWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch( uMsg ) {
case WM_DESTROY :
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)SE_OldPropSheetWndProc);
break;
case WM_DISPLAYCHANGE :
if( GraphicsDialogHandle != NULL )
PostMessage(GraphicsDialogHandle, uMsg, wParam, lParam);
break;
}
return CallWindowProc(SE_OldPropSheetWndProc, hWnd, uMsg, wParam, lParam);
}
bool __cdecl SE_ShowSetupDialog(HWND hParent, bool isDefault) {
static bool initOnce = false;
PROPSHEETHEADER sheetHeader;
PROPSHEETPAGE sheetPages[5];
ChangedAppSettings = SavedAppSettings;
if( isDefault ) {
SE_DefaultGraphicsSettings();
ChangedAppSettings.SoundEnabled = true;
}
memset(sheetPages, 0, sizeof(sheetPages));
memset(&sheetHeader, 0, sizeof(sheetHeader));
for( int i=0; i<5; ++i ) {
sheetPages[i].dwSize = sizeof(PROPSHEETPAGE);
sheetPages[i].dwFlags = PSP_DEFAULT;
sheetPages[i].hInstance = GameModule;
}
sheetPages[0].pszTemplate = MAKEINTRESOURCE(IDD_OPTIONS); // 'Options' Tab
sheetPages[0].pfnDlgProc = SE_OptionsDlgProc;
sheetPages[1].pszTemplate = MAKEINTRESOURCE(IDD_GRAPHICS); // 'Graphics' Tab
sheetPages[1].pfnDlgProc = SE_GraphicsDlgProc;
sheetPages[2].pszTemplate = MAKEINTRESOURCE(IDD_SOUND); // 'Sound' Tab
sheetPages[2].pfnDlgProc = SE_SoundDlgProc;
sheetPages[3].pszTemplate = MAKEINTRESOURCE(IDD_CONTROLS); // 'Controls' Tab
sheetPages[3].pfnDlgProc = SE_ControlsDlgProc;
sheetPages[4].pszTemplate = MAKEINTRESOURCE(IDD_ADVANCED); // 'Advanced' Tab
sheetPages[4].pfnDlgProc = SE_AdvancedDlgProc;
sheetHeader.dwSize = sizeof(PROPSHEETHEADER);
sheetHeader.dwFlags = PSH_USECALLBACK|PSH_NOAPPLYNOW|PSH_PROPSHEETPAGE|PSH_USEHICON;
sheetHeader.hwndParent = hParent;
sheetHeader.hInstance = GameModule;
sheetHeader.hIcon = (HICON)LoadImage(GameModule, MAKEINTRESOURCE(IDI_MAINICON), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
sheetHeader.pszCaption = GameDialogName;
sheetHeader.nPages = 5;
sheetHeader.nStartPage = 1;
sheetHeader.ppsp = sheetPages;
sheetHeader.pfnCallback = SE_PropSheetCallback;
if( !initOnce ) {
InitCommonControls();
SE_RegisterSetupWindowClass();
initOnce = true;
}
IsSetupDialogCentered = false;
if( PropertySheet(&sheetHeader) != 1 )
return false;
SavedAppSettings = ChangedAppSettings;
return true;
}
INT_PTR CALLBACK SE_GraphicsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static bool isGraphicsTest = false;
switch( uMsg ) {
case WM_INITDIALOG :
GraphicsDialogHandle = hwndDlg;
isGraphicsTest = false;
if( !IsSetupDialogCentered ) {
UT_CenterWindow(GetParent(hwndDlg));
IsSetupDialogCentered = true;
}
SE_GraphicsDlgInit(hwndDlg);
return 1;
case WM_DESTROY :
GraphicsDialogHandle = NULL;
break;
case WM_DISPLAYCHANGE :
if( !isGraphicsTest ) {
if( SE_DisplayAdapter != NULL )
WinVidSetDisplayAdapter(&SE_DisplayAdapter->body);
SE_GraphicsDlgUpdate(hwndDlg);
}
break;
case WM_HSCROLL :
if( (HWND)lParam == GetDlgItem(hwndDlg, ID_GRAPH_SLIDER_WINDOWED) ) { // 'Windowed Mode' Slider
SE_GraphicsDlgUpdate(hwndDlg);
}
break;
case WM_COMMAND :
if( HIWORD(wParam) == 0 ) { // Buttons
bool isCheck = ( 1 == SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) );
switch( LOWORD(wParam) ) {
case ID_GRAPH_BUTTON_SWRENDER : // 'Software 256 Colour Renderer' RadioButton
ChangedAppSettings.RenderMode = RM_Software;
SE_GraphicsDlgFullScreenModesUpdate(hwndDlg);
SE_GraphicsDlgUpdate(hwndDlg);
break;
case ID_GRAPH_BUTTON_HWRENDER : // 'Hardware 3D Acceleration' RadioButton
ChangedAppSettings.RenderMode = RM_Hardware;
SE_GraphicsDlgFullScreenModesUpdate(hwndDlg);
SE_GraphicsDlgUpdate(hwndDlg);
break;
#ifndef FEATURE_NOLEGACY_OPTIONS
case ID_GRAPH_BUTTON_PERSPECTIVE : // 'Perspective Correct' CheckBox
ChangedAppSettings.PerspectiveCorrect = isCheck;
SE_GraphicsDlgUpdate(hwndDlg);
break;
case ID_GRAPH_BUTTON_DITHER : // 'Dither' CheckBox
ChangedAppSettings.Dither = isCheck;
SE_GraphicsDlgUpdate(hwndDlg);
break;
#endif // FEATURE_NOLEGACY_OPTIONS
case ID_GRAPH_BUTTON_ZBUFFER : // 'Z Buffer' CheckBox
ChangedAppSettings.ZBuffer = isCheck;
SE_GraphicsDlgUpdate(hwndDlg);
break;
case ID_GRAPH_BUTTON_BILINEAR : // 'Bilinear Filter' CheckBox
ChangedAppSettings.BilinearFiltering = isCheck;
SE_GraphicsDlgUpdate(hwndDlg);
break;
#ifndef FEATURE_NOLEGACY_OPTIONS
case ID_GRAPH_BUTTON_TRIPLEBUFFER : // 'Triple Buffer' CheckBox
ChangedAppSettings.TripleBuffering = isCheck;
SE_GraphicsDlgUpdate(hwndDlg);
break;
#endif // FEATURE_NOLEGACY_OPTIONS
case ID_GRAPH_BUTTON_FULLSCREEN : // 'Full Screen' RadioButton
ChangedAppSettings.FullScreen = true;
SE_GraphicsDlgUpdate(hwndDlg);
break;
case ID_GRAPH_BUTTON_WINDOWED : // 'Windowed' RadioButton
ChangedAppSettings.FullScreen = false;
SE_GraphicsDlgUpdate(hwndDlg);
break;
case ID_GRAPH_BUTTON_TEST : // 'Test' Button
isGraphicsTest = true;
if( 0 != SE_GraphicsTest() ) {
// test fatal error - close Setup Dialog (this never happens though)
PostMessage(SE_PropSheetWndHandle, PSM_PRESSBUTTON, PSBTN_CANCEL, 0);
}
isGraphicsTest = false;
SE_GraphicsDlgUpdate(hwndDlg);
break;
case ID_GRAPH_BUTTON_ASPECT_4_3 : // '4:3' RadioButton
ChangedAppSettings.AspectMode = AM_4_3;
SE_GraphicsDlgUpdate(hwndDlg);
break;
case ID_GRAPH_BUTTON_ASPECT_16_9 : // '16:9' RadioButton
ChangedAppSettings.AspectMode = AM_16_9;
SE_GraphicsDlgUpdate(hwndDlg);
break;
case ID_GRAPH_BUTTON_ASPECT_ANY : // 'Any' RadioButton
ChangedAppSettings.AspectMode = AM_Any;
SE_GraphicsDlgUpdate(hwndDlg);
break;
case ID_GRAPH_BUTTON_DEFAULT : // 'Use Defaults' Button
SE_DefaultGraphicsSettings();
SE_GraphicsDlgInit(hwndDlg);
break;
default:
SE_GraphicsDlgUpdate(hwndDlg);
break;
}
} else if( HIWORD(wParam) == 1 ) { // ComboBoxes
DWORD selectedIndex = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0);
DISPLAY_MODE_NODE *selected = (DISPLAY_MODE_NODE *)SendMessage((HWND)lParam, CB_GETITEMDATA, selectedIndex, 0);
switch( LOWORD(wParam) ) {
case ID_GRAPH_COMBOBOX_ADAPTER : // 'Graphics Card' ComboBox
SE_GraphicsAdapterSet(hwndDlg, (DISPLAY_ADAPTER_NODE *)selected);
SE_GraphicsDlgUpdate(hwndDlg);
break;
case ID_GRAPH_COMBOBOX_FULLSCREEN : // 'Full Screen Mode' ComboBox
ChangedAppSettings.VideoMode = selected;
SE_FullScreenMode = selected->body;
break;
}
}
break;
}
return 0;
}
void __cdecl SE_GraphicsDlgFullScreenModesUpdate(HWND hwndDlg) {
HWND comboBox;
DWORD addedIndex, selectedIndex;
DISPLAY_MODE_LIST *modeList;
DISPLAY_MODE_NODE *mode, *selected;
char stringBuf[256];
comboBox = GetDlgItem(hwndDlg, ID_GRAPH_COMBOBOX_FULLSCREEN); // 'Full Screen Mode' ComboBox
SendMessage(comboBox, CB_RESETCONTENT, 0, 0);
if( ChangedAppSettings.RenderMode == RM_Hardware ) {
modeList = &ChangedAppSettings.PreferredDisplayAdapter->body.hwDispModeList;
} else {
modeList = &ChangedAppSettings.PreferredDisplayAdapter->body.swDispModeList;
#if (DIRECT3D_VERSION >= 0x900)
SE_FullScreenMode.bpp = 32;
#else // (DIRECT3D_VERSION >= 0x900)
SE_FullScreenMode.bpp = 8;
#endif // (DIRECT3D_VERSION >= 0x900)
}
SE_FullScreenMode.vga = VGA_NoVga;
if( modeList->dwCount == 0 || modeList->head == NULL ) {
EnableWindow(comboBox, FALSE);
return;
}
#ifdef FEATURE_NOLEGACY_OPTIONS
if( modeList->head ) {
SE_FullScreenMode.bpp = modeList->head->body.bpp;
SE_FullScreenMode.vga = modeList->head->body.vga;
}
#endif // FEATURE_NOLEGACY_OPTIONS
for( mode = modeList->head; mode; mode = mode->next ) {
if( !CompareVideoModes(&mode->body, &SE_FullScreenMode) )
break;
}
selected = mode ? mode : modeList->tail;
selectedIndex = 0;
for( mode = modeList->head; mode; mode = mode->next ) {
#ifdef FEATURE_NOLEGACY_OPTIONS
wsprintf(stringBuf, "%dx%d", mode->body.width, mode->body.height);
#else // FEATURE_NOLEGACY_OPTIONS
LPCTSTR lpColorString = "";
switch( mode->body.vga ) {
case VGA_NoVga :
switch( mode->body.bpp ) {
case 16:
lpColorString = String_HighColor;
break;
case 24:
case 32:
lpColorString = String_TrueColor;
break;
}
break;
case VGA_256Color :
lpColorString = String_256Color;
break;
case VGA_ModeX :
lpColorString = String_ModeX;
break;
case VGA_Standard :
lpColorString = String_StandardVga;
break;
}
wsprintf(stringBuf, "%dx%d %s", mode->body.width, mode->body.height, lpColorString);
#endif // FEATURE_NOLEGACY_OPTIONS
addedIndex = SendMessage(comboBox, CB_ADDSTRING, 0, (LPARAM)stringBuf);
SendMessage(comboBox, CB_SETITEMDATA, addedIndex, (LPARAM)mode);
if( mode == selected )
selectedIndex = addedIndex;
}
SendMessage(comboBox, CB_SETCURSEL, selectedIndex, 0);
selected = (DISPLAY_MODE_NODE *)SendMessage(comboBox, CB_GETITEMDATA, selectedIndex, 0);
ChangedAppSettings.VideoMode = selected;
if( selected != NULL )
SE_FullScreenMode = selected->body;
EnableWindow(comboBox, (modeList->dwCount > 1));
}
void __cdecl SE_GraphicsAdapterSet(HWND hwndDlg, DISPLAY_ADAPTER_NODE *adapter) {
ChangedAppSettings.PreferredDisplayAdapter = adapter;
SE_GraphicsDlgFullScreenModesUpdate(hwndDlg);
}
void __cdecl SE_GraphicsDlgUpdate(HWND hwndDlg) {
HWND hItem;
char stringWindowRes[16];
bool renderAvailable;
#ifndef FEATURE_NOLEGACY_OPTIONS
bool perspectiveCorrectAvailable;
bool ditherAvailable;
bool tripleBufferingAvailable;
#endif // FEATURE_NOLEGACY_OPTIONS
bool zBufferAvailable;
bool bilinearFilteringAvailable;
bool windowedSizeAvailable;
bool windowedModeAvailable;
bool fullScreenModeAvailable;
bool fullScreenModeListAvailable;
bool windowedModeListAvailable;
DWORD fullScreenVideoModesCount;
DISPLAY_ADAPTER *preferred = &ChangedAppSettings.PreferredDisplayAdapter->body;
#if (DIRECT3D_VERSION >= 0x900)
bool isSWSupported = true;
bool isHWSupported = true;
#else // (DIRECT3D_VERSION >= 0x900)
bool isSWSupported = ( preferred->swDispModeList.dwCount > 0 || preferred->swWindowedSupported );
bool isHWSupported = ( preferred->hwDispModeList.dwCount > 0 || preferred->hwWindowedSupported );
#endif // (DIRECT3D_VERSION >= 0x900)
if( (ChangedAppSettings.RenderMode == RM_Software && !isSWSupported) ||
(ChangedAppSettings.RenderMode == RM_Hardware && !isHWSupported) )
{
ChangedAppSettings.RenderMode = RM_Unknown;
}
if( ChangedAppSettings.RenderMode == RM_Unknown ) {
if( isHWSupported ) {
ChangedAppSettings.RenderMode = RM_Hardware;
} else if( isSWSupported ) {
ChangedAppSettings.RenderMode = RM_Software;
}
SE_GraphicsDlgFullScreenModesUpdate(hwndDlg);
}
if( ChangedAppSettings.RenderMode == RM_Software ) {
// Software Renderer
#ifndef FEATURE_NOLEGACY_OPTIONS
perspectiveCorrectAvailable = true;
ditherAvailable = false;
tripleBufferingAvailable = true;
#endif // FEATURE_NOLEGACY_OPTIONS
zBufferAvailable = false;
bilinearFilteringAvailable = false;
#if (DIRECT3D_VERSION >= 0x900)
windowedModeAvailable = true;
#else // (DIRECT3D_VERSION >= 0x900)
windowedModeAvailable = preferred->swWindowedSupported;
#endif // (DIRECT3D_VERSION >= 0x900)
fullScreenModeAvailable = ( preferred->swDispModeList.dwCount > 0 );
fullScreenVideoModesCount = preferred->swDispModeList.dwCount;
} else if( ChangedAppSettings.RenderMode == RM_Hardware ) {
// Hardware Renderer
#ifndef FEATURE_NOLEGACY_OPTIONS
perspectiveCorrectAvailable = preferred->perspectiveCorrectSupported;
ditherAvailable = preferred->ditherSupported;
tripleBufferingAvailable = true;
#endif // FEATURE_NOLEGACY_OPTIONS
#if (DIRECT3D_VERSION >= 0x900)
zBufferAvailable = true;
bilinearFilteringAvailable = true;
windowedModeAvailable = true;
#else // (DIRECT3D_VERSION >= 0x900)
zBufferAvailable = preferred->zBufferSupported;
bilinearFilteringAvailable = preferred->linearFilterSupported;
windowedModeAvailable = preferred->hwWindowedSupported;
#endif // (DIRECT3D_VERSION >= 0x900)
fullScreenModeAvailable = ( preferred->hwDispModeList.dwCount > 0 );
fullScreenVideoModesCount = preferred->hwDispModeList.dwCount;
} else {
// Unknown Renderer
#ifndef FEATURE_NOLEGACY_OPTIONS
perspectiveCorrectAvailable = false;
ditherAvailable = false;
tripleBufferingAvailable = false;
#endif // FEATURE_NOLEGACY_OPTIONS
zBufferAvailable = false;
bilinearFilteringAvailable = false;
windowedModeAvailable = false;
fullScreenModeAvailable = false;
fullScreenVideoModesCount = 0;
}
if( !windowedModeAvailable ) {
ChangedAppSettings.FullScreen = true;
} else if( !fullScreenModeAvailable ) {
ChangedAppSettings.FullScreen = false;
}
#ifndef FEATURE_NOLEGACY_OPTIONS
if( !ChangedAppSettings.FullScreen )
tripleBufferingAvailable = false;
if( !perspectiveCorrectAvailable )
ChangedAppSettings.PerspectiveCorrect = false;
if( !ditherAvailable )
ChangedAppSettings.Dither = false;
if( !tripleBufferingAvailable )
ChangedAppSettings.TripleBuffering = false;
if( !zBufferAvailable )
ChangedAppSettings.ZBuffer = false;
if( !bilinearFilteringAvailable )
ChangedAppSettings.BilinearFiltering = false;
#endif // FEATURE_NOLEGACY_OPTIONS
renderAvailable = ( ChangedAppSettings.RenderMode != RM_Unknown );
windowedSizeAvailable = ( preferred->screenWidth > 0 );
fullScreenModeListAvailable = ( ChangedAppSettings.FullScreen && renderAvailable );
windowedModeListAvailable = ( !ChangedAppSettings.FullScreen && windowedSizeAvailable );
// 'Graphics Output Method' GroupBox
hItem = GetDlgItem(hwndDlg, ID_GRAPH_GROUPBOX_RENDER);
EnableWindow(hItem, renderAvailable);
// 'Software 256 Colour Renderer' RadioButton
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_SWRENDER);
EnableWindow(hItem, isSWSupported);
SendMessage(hItem, BM_SETCHECK, (ChangedAppSettings.RenderMode == RM_Software), 0);
// 'Hardware 3D Acceleration' RadioButton
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_HWRENDER);
EnableWindow(hItem, isHWSupported);
SendMessage(hItem, BM_SETCHECK, (ChangedAppSettings.RenderMode == RM_Hardware), 0);
// 'Perspective Correct' CheckBox
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_PERSPECTIVE);
#ifdef FEATURE_NOLEGACY_OPTIONS
ShowWindow(hItem, SW_HIDE);
#else // FEATURE_NOLEGACY_OPTIONS
EnableWindow(hItem, perspectiveCorrectAvailable);
SendMessage(hItem, BM_SETCHECK, ChangedAppSettings.PerspectiveCorrect, 0);
#endif // FEATURE_NOLEGACY_OPTIONS
// 'Dither' CheckBox
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_DITHER);
#ifdef FEATURE_NOLEGACY_OPTIONS
POINT checkPos = {0, 0};
MapWindowPoints(hItem, hwndDlg, &checkPos, 1);
ShowWindow(hItem, SW_HIDE);
#else // FEATURE_NOLEGACY_OPTIONS
EnableWindow(hItem, ditherAvailable);
SendMessage(hItem, BM_SETCHECK, ChangedAppSettings.Dither, 0);
#endif // FEATURE_NOLEGACY_OPTIONS
// 'Triple Buffer' CheckBox
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_TRIPLEBUFFER);
#ifdef FEATURE_NOLEGACY_OPTIONS
ShowWindow(hItem, SW_HIDE);
#else // FEATURE_NOLEGACY_OPTIONS
EnableWindow(hItem, tripleBufferingAvailable);
SendMessage(hItem, BM_SETCHECK, ChangedAppSettings.TripleBuffering, 0);
#endif // FEATURE_NOLEGACY_OPTIONS
// 'Z Buffer' CheckBox
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_ZBUFFER);
EnableWindow(hItem, zBufferAvailable);
#ifdef FEATURE_NOLEGACY_OPTIONS
SendMessage(hItem, BM_SETCHECK, zBufferAvailable ? ChangedAppSettings.ZBuffer : 0, 0);
#else // FEATURE_NOLEGACY_OPTIONS
SendMessage(hItem, BM_SETCHECK, ChangedAppSettings.ZBuffer, 0);
#endif // FEATURE_NOLEGACY_OPTIONS
// 'Bilinear Filter' CheckBox
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_BILINEAR);
#ifdef FEATURE_NOLEGACY_OPTIONS
if( checkPos.x && checkPos.y ) {
SetWindowPos(hItem, HWND_TOP, checkPos.x, checkPos.y, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
}
#endif // FEATURE_NOLEGACY_OPTIONS
EnableWindow(hItem, bilinearFilteringAvailable);
#ifdef FEATURE_NOLEGACY_OPTIONS
SendMessage(hItem, BM_SETCHECK, bilinearFilteringAvailable ? ChangedAppSettings.BilinearFiltering : 0, 0);
#else // FEATURE_NOLEGACY_OPTIONS
SendMessage(hItem, BM_SETCHECK, ChangedAppSettings.BilinearFiltering, 0);
#endif // FEATURE_NOLEGACY_OPTIONS
// 'Display Type' GroupBox
hItem = GetDlgItem(hwndDlg, ID_GRAPH_GROUPBOX_DISPLAY);
EnableWindow(hItem, (windowedModeAvailable || fullScreenModeAvailable));
// 'Windowed' RadioButton
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_WINDOWED);
EnableWindow(hItem, windowedModeAvailable);
SendMessage(hItem, BM_SETCHECK, (!ChangedAppSettings.FullScreen && windowedModeAvailable), 0);
// 'Full Screen' RadioButton
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_FULLSCREEN);
EnableWindow(hItem, fullScreenModeAvailable);
SendMessage(hItem, BM_SETCHECK, (ChangedAppSettings.FullScreen && fullScreenModeAvailable), 0);
// 'Full Screen Mode' GroupBox
hItem = GetDlgItem(hwndDlg, ID_GRAPH_GROUPBOX_FULLSCREEN);
EnableWindow(hItem, fullScreenModeListAvailable);
// 'Full Screen Mode' ComboBox
hItem = GetDlgItem(hwndDlg, ID_GRAPH_COMBOBOX_FULLSCREEN);
EnableWindow(hItem, (fullScreenModeListAvailable && fullScreenVideoModesCount > 1));
// 'Windowed Mode' GroupBox
hItem = GetDlgItem(hwndDlg, ID_GRAPH_GROUPBOX_WINDOWED);
EnableWindow(hItem, windowedModeListAvailable);
// 'Small Windowed Mode' Static
hItem = GetDlgItem(hwndDlg, ID_GRAPH_STATIC_RES_SMALL);
EnableWindow(hItem, windowedModeListAvailable);
// 'Large Windowed Mode' Static
hItem = GetDlgItem(hwndDlg, ID_GRAPH_STATIC_RES_LARGE);
EnableWindow(hItem, windowedModeListAvailable);
// 'Windowed Mode Resolution' Static
hItem = GetDlgItem(hwndDlg, ID_GRAPH_STATIC_RESOLUTION);
EnableWindow(hItem, windowedModeListAvailable);
// '4:3 Aspect' RadioButton
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_ASPECT_4_3);
EnableWindow(hItem, windowedModeListAvailable);
SendMessage(hItem, BM_SETCHECK, (ChangedAppSettings.AspectMode == AM_4_3), 0);
// '16:9 Aspect' RadioButton
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_ASPECT_16_9);
EnableWindow(hItem, windowedModeListAvailable);
SendMessage(hItem, BM_SETCHECK, (ChangedAppSettings.AspectMode == AM_16_9), 0);
// 'Any Aspect' RadioButton
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_ASPECT_ANY);
EnableWindow(hItem, windowedModeListAvailable);
SendMessage(hItem, BM_SETCHECK, (ChangedAppSettings.AspectMode == AM_Any), 0);
// 'Windowed Mode' Slider
hItem = GetDlgItem(hwndDlg, ID_GRAPH_SLIDER_WINDOWED);
EnableWindow(hItem, windowedModeListAvailable);
if( windowedSizeAvailable ) {
int minPosition = 0;
int maxWidth = preferred->screenWidth;
int maxPosition = ((maxWidth - 320 ) / 32) & 0xFFFF;
SendMessage(hItem, TBM_SETRANGE, 1, ((maxPosition<<0x10)|minPosition));
DWORD sliderPosition = SendMessage(hItem, TBM_GETPOS, 0, 0);
ChangedAppSettings.WindowWidth = sliderPosition * 32 + 320;
switch( ChangedAppSettings.AspectMode ) {
case AM_4_3 :
ChangedAppSettings.WindowHeight = ChangedAppSettings.WindowWidth*3/4;
break;
case AM_16_9 :
ChangedAppSettings.WindowHeight = ChangedAppSettings.WindowWidth*9/16;
break;
case AM_Any :
#ifdef FEATURE_NOLEGACY_OPTIONS
ChangedAppSettings.WindowHeight = ChangedAppSettings.WindowWidth*3/4;
#else // !FEATURE_NOLEGACY_OPTIONS
ChangedAppSettings.WindowHeight = ChangedAppSettings.WindowWidth;
#endif // FEATURE_NOLEGACY_OPTIONS
break;
}
}
// 'Windowed Mode Resolution' Static
if( windowedSizeAvailable )
wsprintf(stringWindowRes, "%dx%d", ChangedAppSettings.WindowWidth, ChangedAppSettings.WindowHeight);
else
*stringWindowRes = 0;
SetDlgItemText(hwndDlg, ID_GRAPH_STATIC_RESOLUTION, stringWindowRes);
// 'Graphics Options' GroupBox
hItem = GetDlgItem(hwndDlg, ID_GRAPH_GROUPBOX_OPTIONS);
EnableWindow(hItem, renderAvailable);
// 'Test Settings' GroupBox
hItem = GetDlgItem(hwndDlg, ID_GRAPH_GROUPBOX_TEST);
EnableWindow(hItem, renderAvailable);
// 'Test' Button
hItem = GetDlgItem(hwndDlg, ID_GRAPH_BUTTON_TEST);
EnableWindow(hItem, renderAvailable);
// 'Test Result' Static
hItem = GetDlgItem(hwndDlg, ID_GRAPH_STATIC_TESTRESULT);
EnableWindow(hItem, renderAvailable);
// 'Test Details' Static
hItem = GetDlgItem(hwndDlg, ID_GRAPH_STATIC_TESTDETAIL);
EnableWindow(hItem, renderAvailable);
}
void __cdecl SE_GraphicsDlgInit(HWND hwndDlg) {
HWND comboBox, slider;
DWORD addedIndex, selectedIndex;
DISPLAY_ADAPTER_NODE *adapter, *selected;
comboBox = GetDlgItem(hwndDlg, ID_GRAPH_COMBOBOX_ADAPTER); // 'Graphics Card' ComboBox
SendMessage(comboBox, CB_RESETCONTENT, 0, 0);
selectedIndex = 0;
SE_DisplayAdapter = NULL;
for( adapter = DisplayAdapterList.head; adapter; adapter = adapter->next ) {
addedIndex = SendMessage(comboBox, CB_ADDSTRING, 0, (LPARAM)adapter->body.driverDescription.lpString);
SendMessage(comboBox, CB_SETITEMDATA, addedIndex, (LPARAM)adapter);
if( adapter == ChangedAppSettings.PreferredDisplayAdapter )
selectedIndex = addedIndex;
if( adapter->body.lpAdapterGuid == NULL )
SE_DisplayAdapter = adapter;
WinVidSetDisplayAdapter(&adapter->body);
}
SendMessage(comboBox, CB_SETCURSEL, selectedIndex, 0);
EnableWindow(comboBox, (DisplayAdapterList.dwCount > 1)); // NOTE: original was be >0. But there is no reason to select 1/1 adapter
if( DisplayAdapterList.dwCount > 0 ) {
selected = (DISPLAY_ADAPTER_NODE *)SendMessage(comboBox, CB_GETITEMDATA, selectedIndex, 0);
if( ChangedAppSettings.VideoMode != NULL ) {
SE_FullScreenMode = ChangedAppSettings.VideoMode->body;
SE_GraphicsAdapterSet(hwndDlg, selected);
} else {
SE_FullScreenMode.width = 640;
SE_FullScreenMode.height = 480;
SE_FullScreenMode.bpp = 0;
SE_FullScreenMode.vga = VGA_NoVga;
SE_GraphicsAdapterSet(hwndDlg, selected);
}
}
slider = GetDlgItem(hwndDlg, ID_GRAPH_SLIDER_WINDOWED); // 'Windowed Mode' Slider
SendMessage(slider, TBM_SETPOS, TRUE, (ChangedAppSettings.WindowWidth - 320) / 32);
SE_GraphicsDlgUpdate(hwndDlg);
}
INT_PTR CALLBACK SE_SoundDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static HWND hImage = NULL;
switch( uMsg ) {
case WM_INITDIALOG :
SoundDialogHandle = hwndDlg;
hImage = GetDlgItem(hwndDlg, ID_SOUND_WINDOW_IMAGE);
SE_SoundDlgInit(hwndDlg);
return 1;
case WM_COMMAND :
if( HIWORD(wParam) == 0 ) { // Buttons
switch( LOWORD(wParam) ) {
case ID_SOUND_BUTTON_SFX_ENABLE : // 'Enable Sound' CheckBox
ChangedAppSettings.SoundEnabled = (1 == SendMessage((HWND)lParam, BM_GETCHECK, 0, 0));
break;
case ID_SOUND_BUTTON_MIC_CAMERA : // 'Camera Microphone Position' RadioButton
ChangedAppSettings.LaraMic = false;
break;
case ID_SOUND_BUTTON_MIC_LARA : // 'Lara Microphone Position' RadioButton
ChangedAppSettings.LaraMic = true;
break;
case ID_SOUND_BUTTON_TEST : // 'Test' Button
if( 0 != SE_SoundTest() ) {
// test fatal error - close Setup Dialog (this never happens though)
PostMessage(SE_PropSheetWndHandle, PSM_PRESSBUTTON, PSBTN_CANCEL, 0);
}
SendMessage((HWND)lParam, BM_SETCHECK, 0, 0);
break;
}
} else if( HIWORD(wParam) == 1 ) { // ComboBoxes
if( LOWORD(wParam) == ID_SOUND_COMBOBOX_ADAPTER ) { // 'Sound Card' ComboBox
DWORD index = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0);
SOUND_ADAPTER_NODE *adapter = (SOUND_ADAPTER_NODE *)SendMessage((HWND)lParam, CB_GETITEMDATA, index, 0);
SE_SoundAdapterSet(hwndDlg, adapter);
SE_SoundDlgUpdate(hwndDlg);
}
}
break;
}
SE_PassMessageToImage(hImage, uMsg, wParam);
return 0;
}
void __cdecl SE_SoundAdapterSet(HWND hwndDlg, SOUND_ADAPTER_NODE *adapter) {
ChangedAppSettings.PreferredSoundAdapter = adapter;
}
void __cdecl SE_SoundDlgUpdate(HWND hwndDlg) {
HWND hItem;
bool isAvailable = ( SoundAdapterList.dwCount > 0 );
// 'Microphone Position' GroupBox
hItem = GetDlgItem(hwndDlg, ID_SOUND_GROUPBOX_MIC);
EnableWindow(hItem, isAvailable);
// 'Sound Output' GroupBox
hItem = (HWND)GetDlgItem(hwndDlg, ID_SOUND_GROUPBOX_OUTPUT);
EnableWindow(hItem, isAvailable);
// 'Enable Sound' CheckBox
hItem = (HWND)GetDlgItem(hwndDlg, ID_SOUND_BUTTON_SFX_ENABLE);
EnableWindow(hItem, isAvailable);
SendMessage(hItem, BM_SETCHECK, (isAvailable && ChangedAppSettings.SoundEnabled), 0);
// 'Camera' RadioButton
hItem = (HWND)GetDlgItem(hwndDlg, ID_SOUND_BUTTON_MIC_CAMERA);
EnableWindow(hItem, isAvailable);
SendMessage(hItem, BM_SETCHECK, !ChangedAppSettings.LaraMic, 0);
// 'Lara' RadioButton
hItem = (HWND)GetDlgItem(hwndDlg, ID_SOUND_BUTTON_MIC_LARA);
EnableWindow(hItem, isAvailable);
SendMessage(hItem, BM_SETCHECK, ChangedAppSettings.LaraMic, 0);
// 'Test' Button
hItem = (HWND)GetDlgItem(hwndDlg, ID_SOUND_BUTTON_TEST);
EnableWindow(hItem, isAvailable);
}
void __cdecl SE_SoundDlgInit(HWND hwndDlg) {
HWND comboBox;
DWORD addedIndex, selectedIndex;
SOUND_ADAPTER_NODE *adapter, *selected;
// 'Sound Adapter' ComboBox
comboBox = GetDlgItem(hwndDlg, ID_SOUND_COMBOBOX_ADAPTER);
SendMessage(comboBox, CB_RESETCONTENT, 0, 0);
selectedIndex = 0;
for( adapter = SoundAdapterList.head; adapter; adapter = adapter->next ) {
addedIndex = SendMessage(comboBox, CB_ADDSTRING, 0, (LPARAM)adapter->body.description.lpString);
SendMessage(comboBox, CB_SETITEMDATA, addedIndex, (LPARAM)adapter);
if( adapter == ChangedAppSettings.PreferredSoundAdapter )
selectedIndex = addedIndex;
}
SendMessage(comboBox, CB_SETCURSEL, selectedIndex, 0);
EnableWindow(comboBox, (SoundAdapterList.dwCount > 1));
if( SoundAdapterList.dwCount > 0 ) {
selected = (SOUND_ADAPTER_NODE *)SendMessage(comboBox, CB_GETITEMDATA, selectedIndex, 0);
SE_SoundAdapterSet(hwndDlg, selected);
}
SE_SoundDlgUpdate(hwndDlg);
}
INT_PTR CALLBACK SE_ControlsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static HWND hImage = NULL;
switch( uMsg ) {
case WM_INITDIALOG :
hImage = GetDlgItem(hwndDlg, ID_CTRLS_WINDOW_IMAGE);
SE_ControlsDlgInit(hwndDlg);
return 1;
case WM_COMMAND :
if( HIWORD(wParam) == 0 ) { // Buttons
switch( LOWORD(wParam) ) {
case ID_CTRLS_BUTTON_JOYSTICK_ENABLE : // 'Enable Joystick' checkbox
ChangedAppSettings.JoystickEnabled = (1 == SendMessage((HWND)lParam, BM_GETCHECK, 0, 0));
break;
case ID_CTRLS_BUTTON_CTL_PANEL : // 'Run Control Panel' Button
DInputCreate();
WinInRunControlPanel(hwndDlg);
DInputRelease();
break;
}
} else if( HIWORD(wParam) == 1 ) { // ComboBoxes
if( LOWORD(wParam) == ID_CTRLS_COMBOBOX_JOYSTICK ) { // 'Joystick' ComboBox
DWORD index = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0);
JOYSTICK_NODE *joystick = (JOYSTICK_NODE *)SendMessage((HWND)lParam, CB_GETITEMDATA, index, 0);
SE_ControlsJoystickSet(hwndDlg, joystick);
SE_ControlsDlgUpdate(hwndDlg);
}
}
}
SE_PassMessageToImage(hImage, uMsg, wParam);
return 0;
}
void __cdecl SE_ControlsJoystickSet(HWND hwndDlg, JOYSTICK_NODE *joystick) {
ChangedAppSettings.PreferredJoystick = joystick;
}
void __cdecl SE_ControlsDlgUpdate(HWND hwndDlg) {
bool isAvailable = ( JoystickList.dwCount > 0 );
bool isCheck = ( isAvailable && ChangedAppSettings.JoystickEnabled );
// 'Enable Joystick' CheckBox
HWND checkBox = GetDlgItem(hwndDlg, ID_CTRLS_BUTTON_JOYSTICK_ENABLE);
#ifdef FEATURE_INPUT_IMPROVED
bool isDInput = false;
if( isAvailable && ChangedAppSettings.PreferredJoystick ) {
isDInput = ChangedAppSettings.PreferredJoystick->body.iface == JOY_DirectInput;
}
EnableWindow(GetDlgItem(hwndDlg, ID_CTRLS_BUTTON_CTL_PANEL), isDInput);
#endif // FEATURE_INPUT_IMPROVED
EnableWindow(checkBox, isAvailable);
SendMessage(checkBox, BM_SETCHECK, isCheck, 0);
}
void __cdecl SE_ControlsDlgInit(HWND hwndDlg) {
HWND comboBox;
DWORD addedIndex, selectedIndex;
JOYSTICK_NODE *joystick, *selected;
comboBox = GetDlgItem(hwndDlg, ID_CTRLS_COMBOBOX_JOYSTICK); // 'Joystick' ComboBox
SendMessage(comboBox, CB_RESETCONTENT, 0, 0);
selectedIndex = 0;
for( joystick = JoystickList.head; joystick; joystick = joystick->next ) {
addedIndex = SendMessage(comboBox, CB_ADDSTRING, 0, (LPARAM)joystick->body.productName.lpString);
SendMessage(comboBox, CB_SETITEMDATA, addedIndex, (LPARAM)joystick);
if( joystick == ChangedAppSettings.PreferredJoystick )
selectedIndex = addedIndex;
}
SendMessage(comboBox, CB_SETCURSEL, selectedIndex, 0);
EnableWindow(comboBox, (JoystickList.dwCount > 1)); // NOTE: original was >0. But there is no reason to select 1/1 joystick
if( JoystickList.dwCount > 0 ) {
selected = (JOYSTICK_NODE *)SendMessage(comboBox, CB_GETITEMDATA, selectedIndex, 0);
SE_ControlsJoystickSet(hwndDlg, selected);
}
SE_ControlsDlgUpdate(hwndDlg);
}
INT_PTR CALLBACK SE_OptionsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
HWND hImage = NULL;
switch( uMsg ) {
case WM_INITDIALOG :
hImage = GetDlgItem(hwndDlg, ID_OPTNS_WINDOW_IMAGE);
if( !IsSetupDialogCentered ) {
UT_CenterWindow(GetParent(hwndDlg));
IsSetupDialogCentered = true;
}
return 1;
case WM_NOTIFY :
if( ((LPNMHDR)lParam)->code == ((UINT)PSN_SETACTIVE) )
SE_OptionsDlgUpdate(hwndDlg);
break;
}
SE_PassMessageToImage(hImage, uMsg, wParam);
return 0;
}
void __cdecl SE_OptionsDlgUpdate(HWND hwndDlg) {
LPCTSTR lpString;
char resultString[256];
if( ChangedAppSettings.PreferredDisplayAdapter != NULL ) {
// 'Graphics' Static
SetDlgItemText(hwndDlg, ID_OPTNS_STATIC_GRAPHICS, ChangedAppSettings.PreferredDisplayAdapter->body.driverDescription.lpString);
// 'Using:' Static
if( ChangedAppSettings.RenderMode == RM_Hardware )
lpString = String_Hardware3d;
else
lpString = String_Software3d;
SetDlgItemText(hwndDlg, ID_OPTNS_STATIC_USING, lpString);
// 'Display:' Static
if( ChangedAppSettings.FullScreen && ChangedAppSettings.VideoMode )
#ifdef FEATURE_NOLEGACY_OPTIONS
wsprintf(resultString, "%s %dx%d", String_FullScreen, ChangedAppSettings.VideoMode->body.width, ChangedAppSettings.VideoMode->body.height);
#else // FEATURE_NOLEGACY_OPTIONS
wsprintf(resultString, "%s %dx%dx%d", String_FullScreen, ChangedAppSettings.VideoMode->body.width, ChangedAppSettings.VideoMode->body.height, ChangedAppSettings.VideoMode->body.bpp);
#endif // FEATURE_NOLEGACY_OPTIONS
else
wsprintf(resultString, "%s %dx%d%s", String_Windowed, ChangedAppSettings.WindowWidth, ChangedAppSettings.WindowHeight, Strings_Aspect[ChangedAppSettings.AspectMode]);
SetDlgItemText(hwndDlg, ID_OPTNS_STATIC_DISPLAY, resultString);
// 'Options:' Static
bool isNext = false;
LPTSTR pResultString = resultString;
SE_OptionsStrCat(&pResultString, ChangedAppSettings.ZBuffer, &isNext, String_ZBuffered);
SE_OptionsStrCat(&pResultString, ChangedAppSettings.BilinearFiltering, &isNext, String_BilinearFiltered);
#ifndef FEATURE_NOLEGACY_OPTIONS
SE_OptionsStrCat(&pResultString, ChangedAppSettings.Dither, &isNext, String_Dithered);
SE_OptionsStrCat(&pResultString, ChangedAppSettings.TripleBuffering, &isNext, String_TripleBuffered);
SE_OptionsStrCat(&pResultString, ChangedAppSettings.PerspectiveCorrect, &isNext, String_PerspectiveCorrect);
#endif // FEATURE_NOLEGACY_OPTIONS
*pResultString = 0;
SetDlgItemText(hwndDlg, ID_OPTNS_STATIC_OPTIONS, resultString);
} else {
// 'Graphics' Static
SetDlgItemText(hwndDlg, ID_OPTNS_STATIC_GRAPHICS, String_None);
// 'Using:' Static
SetDlgItemText(hwndDlg, ID_OPTNS_STATIC_USING, String_NA);
// 'Display:' Static
SetDlgItemText(hwndDlg, ID_OPTNS_STATIC_DISPLAY, String_NA);
// 'Options:' Static
SetDlgItemText(hwndDlg, ID_OPTNS_STATIC_OPTIONS, String_NA);
}
// 'Sound:' Static
if( ChangedAppSettings.PreferredSoundAdapter != NULL )
lpString = ChangedAppSettings.PreferredSoundAdapter->body.description.lpString;
else
lpString = String_None;
SetDlgItemText(hwndDlg, ID_OPTNS_STATIC_SOUND, lpString);
// 'Sound Effects:' Static
if( ChangedAppSettings.SoundEnabled )
lpString = String_Enabled;
else
lpString = String_Disabled;
SetDlgItemText(hwndDlg, ID_OPTNS_STATIC_SFX, lpString);
// 'Microphone Position' Static
if( ChangedAppSettings.LaraMic )
lpString = String_Lara;
else
lpString = String_Camera;
SetDlgItemText(hwndDlg, ID_OPTNS_STATIC_MIC, lpString);
// 'Joystick' Static
if( ChangedAppSettings.PreferredJoystick != NULL )
lpString = ChangedAppSettings.PreferredJoystick->body.productName.lpString;
else
lpString = String_None;
SetDlgItemText(hwndDlg, ID_OPTNS_STATIC_JOYSTICK, lpString);
}
void __cdecl SE_OptionsStrCat(LPTSTR *dstString, bool isEnabled, bool *isNext, LPCTSTR srcString) {
if( !isEnabled )
return;
if( *isNext ) {
*(*dstString)++ = ',';
*(*dstString)++ = ' ';
}
while( *srcString )
*(*dstString)++ = *srcString++;
*isNext = true;
}
INT_PTR CALLBACK SE_AdvancedDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static HWND hImage = NULL;
switch( uMsg ) {
case WM_INITDIALOG :
hImage = GetDlgItem(hwndDlg, ID_ADVNC_WINDOW_IMAGE);
SE_AdvancedDlgInit(hwndDlg);
return 1;
case WM_COMMAND :
if( HIWORD(wParam) == 0 ) { // Buttons
bool isCheck = ( 1 == SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) );
switch( LOWORD(wParam) ) {
#ifndef FEATURE_NOLEGACY_OPTIONS
case ID_ADVNC_BUTTON_16BIT_DISABLE : // 'Disable 16 bit textures' CheckBox
ChangedAppSettings.Disable16BitTextures = isCheck;
break;
case ID_ADVNC_BUTTON_SORT_DISABLE : // 'Don't sort transparent polys' CheckBox
ChangedAppSettings.DontSortPrimitives = isCheck;
break;
#endif // FEATURE_NOLEGACY_OPTIONS
case ID_ADVNC_BUTTON_FMV_DISABLE : // 'Disable FMV' CheckBox
ChangedAppSettings.DisableFMV = isCheck;
break;
#ifndef FEATURE_NOLEGACY_OPTIONS
case ID_ADVNC_BUTTON_ADJUST_DISABLE : // 'Don't adjust' RadioButton
ChangedAppSettings.TexelAdjustMode = TAM_Disabled;
break;
case ID_ADVNC_BUTTON_ADJUST_BILINEAR : // 'Adjust when bilinear filtering' RadioButton
ChangedAppSettings.TexelAdjustMode = TAM_BilinearOnly;
break;
case ID_ADVNC_BUTTON_ADJUST_ALWAYS : // 'Always adjust' RadioButton
ChangedAppSettings.TexelAdjustMode = TAM_Always;
break;
#endif // FEATURE_NOLEGACY_OPTIONS
}
SE_AdvancedDlgUpdate(hwndDlg);
}
break;
}
SE_PassMessageToImage(hImage, uMsg, wParam);
return 0;
}
void __cdecl SE_AdvancedDlgUpdate(HWND hwndDlg) {
// 'Disable 16 bit textures' CheckBox
CheckDlgButton(hwndDlg, ID_ADVNC_BUTTON_16BIT_DISABLE, ChangedAppSettings.Disable16BitTextures);
// 'Don't sort transparent polys' CheckBox
CheckDlgButton(hwndDlg, ID_ADVNC_BUTTON_SORT_DISABLE, ChangedAppSettings.DontSortPrimitives);
// 'Disable FMV' CheckBox
CheckDlgButton(hwndDlg, ID_ADVNC_BUTTON_FMV_DISABLE, ChangedAppSettings.DisableFMV);
// 'Don't adjust' RadioButton
CheckDlgButton(hwndDlg, ID_ADVNC_BUTTON_ADJUST_DISABLE, ChangedAppSettings.TexelAdjustMode == TAM_Disabled);
// 'Adjust when bilinear filtering' RadioButton
CheckDlgButton(hwndDlg, ID_ADVNC_BUTTON_ADJUST_BILINEAR, ChangedAppSettings.TexelAdjustMode == TAM_BilinearOnly);
// 'Always adjust' RadioButton
CheckDlgButton(hwndDlg, ID_ADVNC_BUTTON_ADJUST_ALWAYS, ChangedAppSettings.TexelAdjustMode == TAM_Always);
#ifdef FEATURE_NOLEGACY_OPTIONS
EnableWindow(GetDlgItem(hwndDlg, ID_ADVNC_BUTTON_16BIT_DISABLE), FALSE);
EnableWindow(GetDlgItem(hwndDlg, ID_ADVNC_BUTTON_SORT_DISABLE), FALSE);
EnableWindow(GetDlgItem(hwndDlg, ID_ADVNC_BUTTON_ADJUST_DISABLE), FALSE);
EnableWindow(GetDlgItem(hwndDlg, ID_ADVNC_BUTTON_ADJUST_BILINEAR), FALSE);
EnableWindow(GetDlgItem(hwndDlg, ID_ADVNC_BUTTON_ADJUST_ALWAYS), FALSE);
#endif // FEATURE_NOLEGACY_OPTIONS
}
void __cdecl SE_AdvancedDlgInit(HWND hwndDlg) {
SE_AdvancedDlgUpdate(hwndDlg);
}
HWND __cdecl SE_FindSetupDialog() {
return FindWindow(WC_DIALOG, GameDialogName);
}
/*
* Inject function
*/
void Inject_SetupDlg() {
INJECT(0x00452480, OpenGameRegistryKey);
INJECT(0x004524D0, CloseGameRegistryKey);
INJECT(0x004524E0, SE_WriteAppSettings);
INJECT(0x00452760, SE_ReadAppSettings);
INJECT(0x00452AC0, SE_GraphicsTestStart);
INJECT(0x00452B90, SE_GraphicsTestFinish);
INJECT(0x00452BB0, SE_GraphicsTestExecute);
INJECT(0x00452BC0, SE_GraphicsTest);
INJECT(0x00452C20, SE_DefaultGraphicsSettings);
INJECT(0x00452D70, SE_SoundTestStart);
INJECT(0x00452E30, SE_SoundTestFinish);
INJECT(0x00452E40, SE_SoundTestExecute);
INJECT(0x00452E80, SE_SoundTest);
INJECT(0x00452EE0, SE_PropSheetCallback);
INJECT(0x00452F20, SE_NewPropSheetWndProc);
INJECT(0x00452F80, SE_ShowSetupDialog);
INJECT(0x00453150, SE_GraphicsDlgProc);
INJECT(0x00453560, SE_GraphicsDlgFullScreenModesUpdate);
INJECT(0x00453750, SE_GraphicsAdapterSet);
INJECT(0x00453770, SE_GraphicsDlgUpdate);
INJECT(0x00453EB0, SE_GraphicsDlgInit);
INJECT(0x00454030, SE_SoundDlgProc);
INJECT(0x004541C0, SE_SoundAdapterSet);
INJECT(0x004541D0, SE_SoundDlgUpdate);
INJECT(0x004542F0, SE_SoundDlgInit);
INJECT(0x004543B0, SE_ControlsDlgProc);
INJECT(0x004544D0, SE_ControlsJoystickSet);
INJECT(0x004544D0, SE_ControlsDlgUpdate);
INJECT(0x00454540, SE_ControlsDlgInit);
INJECT(0x00454690, SE_OptionsDlgUpdate);
INJECT(0x004548D0, SE_OptionsStrCat);
INJECT(0x00454920, SE_AdvancedDlgProc);
INJECT(0x00454A30, SE_AdvancedDlgUpdate);
INJECT(0x00454AE0, SE_AdvancedDlgInit);
INJECT(0x00454AF0, SE_FindSetupDialog);
}
================================================
FILE: specific/setupdlg.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef SETUPDLG_H_INCLUDED
#define SETUPDLG_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
bool __cdecl OpenGameRegistryKey(LPCTSTR key); // 0x00452480
LONG __cdecl CloseGameRegistryKey(); // 0x004524D0
bool __cdecl SE_WriteAppSettings(APP_SETTINGS *settings); // 0x004524E0
int __cdecl SE_ReadAppSettings(APP_SETTINGS *settings); // 0x00452760
bool __cdecl SE_GraphicsTestStart(); // 0x00452AC0
void __cdecl SE_GraphicsTestFinish(); // 0x00452B90
int __cdecl SE_GraphicsTestExecute(); // 0x00452BB0
int __cdecl SE_GraphicsTest(); // 0x00452BC0
void __cdecl SE_DefaultGraphicsSettings(); // 0x00452C20
bool __cdecl SE_SoundTestStart(); // 0x00452D70
void __cdecl SE_SoundTestFinish(); // 0x00452E30
int __cdecl SE_SoundTestExecute(); // 0x00452E40
int __cdecl SE_SoundTest(); // 0x00452E80
int CALLBACK SE_PropSheetCallback(HWND hwndDlg, UINT uMsg, LPARAM lParam); // 0x00452EE0
LRESULT CALLBACK SE_NewPropSheetWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); // 0x00452F20
bool __cdecl SE_ShowSetupDialog(HWND hParent, bool isDefault); // 0x00452F80
INT_PTR CALLBACK SE_GraphicsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); // 0x00453150
void __cdecl SE_GraphicsDlgFullScreenModesUpdate(HWND hwndDlg); // 0x00453560
void __cdecl SE_GraphicsAdapterSet(HWND hwndDlg, DISPLAY_ADAPTER_NODE *adapter); // 0x00453750
void __cdecl SE_GraphicsDlgUpdate(HWND hwndDlg); // 0x00453770
void __cdecl SE_GraphicsDlgInit(HWND hwndDlg); // 0x00453EB0
INT_PTR CALLBACK SE_SoundDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); // 0x00454030
void __cdecl SE_SoundAdapterSet(HWND hwndDlg, SOUND_ADAPTER_NODE *adapter); // 0x004541C0
void __cdecl SE_SoundDlgUpdate(HWND hwndDlg); // 0x004541D0
void __cdecl SE_SoundDlgInit(HWND hwndDlg); // 0x004542F0
INT_PTR CALLBACK SE_ControlsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); // 0x004543B0
void __cdecl SE_ControlsJoystickSet(HWND hwndDlg, JOYSTICK_NODE *joystick); // 0x004544C0
void __cdecl SE_ControlsDlgUpdate(HWND hwndDlg); // 0x004544D0
void __cdecl SE_ControlsDlgInit(HWND hwndDlg); // 0x00454540
INT_PTR CALLBACK SE_OptionsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); // 0x00454600
void __cdecl SE_OptionsDlgUpdate(HWND hwndDlg); // 0x00454690
void __cdecl SE_OptionsStrCat(LPTSTR *dstString, bool isEnabled, bool *isNext, const LPCTSTR srcString); // 0x004548D0
INT_PTR CALLBACK SE_AdvancedDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); // 0x00454920
void __cdecl SE_AdvancedDlgUpdate(HWND hwndDlg); // 0x00454A30
void __cdecl SE_AdvancedDlgInit(HWND hwndDlg); // 0x00454AE0
HWND __cdecl SE_FindSetupDialog(); // 0x00454AF0
#endif // SETUPDLG_H_INCLUDED
================================================
FILE: specific/setupwnd.cpp
================================================
/*
* Copyright (c) 2017 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/setupwnd.h"
#include "specific/utils.h"
#include "global/vars.h"
#include
#define WINLONG_X (0)
#define WINLONG_Y (4)
#define WINLONG_BMP (8)
static void SE_CenterBitmapResource(HWND hWnd, BITMAP_RESOURCE *pBitmap, LPCTSTR imageName) {
int x, y, width, height;
if( pBitmap == NULL || imageName == NULL || *imageName == 0 )
return;
SE_LoadBitmapResource(pBitmap, imageName);
x = (int)GetWindowLong(hWnd, WINLONG_X);
y = (int)GetWindowLong(hWnd, WINLONG_Y);
width = pBitmap->bmpInfo->bmiHeader.biWidth + 4;
height = pBitmap->bmpInfo->bmiHeader.biHeight + 4;
MoveWindow(hWnd, x-width/2, y-height/2, width, height, TRUE);
}
void __thiscall SE_ReleaseBitmapResource(BITMAP_RESOURCE *bmpRsrc) {
if( bmpRsrc->hPalette != NULL ) {
DeleteObject(bmpRsrc->hPalette);
bmpRsrc->hPalette = NULL;
}
if( bmpRsrc->bmpData != NULL && (bmpRsrc->flags & 1) != 0 ) {
free(bmpRsrc->bmpData);
bmpRsrc->bmpData = NULL;
}
}
void __thiscall SE_LoadBitmapResource(BITMAP_RESOURCE *bmpRsrc, LPCTSTR lpName) {
static LOGPALETTE *logPalette = (LOGPALETTE *)malloc(sizeof(LOGPALETTE) + sizeof(PALETTEENTRY)*255);
PALETTEENTRY *bmpPalette;
BYTE *bitmap;
if( bmpRsrc->hPalette != NULL ) {
DeleteObject(bmpRsrc->hPalette);
bmpRsrc->hPalette = NULL;
}
bitmap = (BYTE *)UT_LoadResource(lpName, RT_BITMAP);
bmpRsrc->bmpInfo = (PBITMAPINFO)bitmap;
bmpRsrc->flags = 0;
if( bitmap == NULL )
return;
bmpPalette = (PALETTEENTRY *)(bitmap + sizeof(BITMAPINFOHEADER));
bmpRsrc->bmpData = bitmap + sizeof(BITMAPINFOHEADER) + sizeof(PALETTEENTRY)*256;
logPalette->palVersion = 0x0300;
logPalette->palNumEntries = 256;
for( int i=0; i<256; ++i ) {
logPalette->palPalEntry[i].peRed = bmpPalette[i].peRed;
logPalette->palPalEntry[i].peGreen = bmpPalette[i].peGreen;
logPalette->palPalEntry[i].peBlue = bmpPalette[i].peBlue;
logPalette->palPalEntry[i].peFlags = (i>=10 && i<246) ? PC_NOCOLLAPSE : 0;
}
bmpRsrc->hPalette = CreatePalette(logPalette);
}
void __thiscall SE_DrawBitmap(BITMAP_RESOURCE *bmpRsrc, HDC hdc, int x, int y) {
int width, height;
HPALETTE hPalette;
if( bmpRsrc->hPalette == NULL)
return;
width = bmpRsrc->bmpInfo->bmiHeader.biWidth;
height = bmpRsrc->bmpInfo->bmiHeader.biHeight;
hPalette = SelectPalette(hdc, bmpRsrc->hPalette, FALSE);
RealizePalette(hdc);
SetDIBitsToDevice(hdc, x, y, width, height, 0, 0, 0, height, bmpRsrc->bmpData, bmpRsrc->bmpInfo, DIB_RGB_COLORS);
SelectPalette(hdc, hPalette, FALSE);
}
void __thiscall SE_UpdateBitmapPalette(BITMAP_RESOURCE *bmpRsrc, HWND hWnd, HWND hSender) {
if( bmpRsrc->hPalette != NULL && hSender != hWnd )
SE_ChangeBitmapPalette(bmpRsrc, hWnd);
}
void __thiscall SE_ChangeBitmapPalette(BITMAP_RESOURCE *bmpRsrc, HWND hWnd) {
HDC hdc;
HPALETTE hPalette;
if( bmpRsrc->hPalette == NULL)
return;
hdc = GetDC(hWnd);
hPalette = SelectPalette(hdc, bmpRsrc->hPalette, FALSE);
UnrealizeObject(bmpRsrc->hPalette);
RealizePalette(hdc);
SelectPalette(hdc, hPalette, FALSE);
ReleaseDC(hWnd, hdc);
}
bool __cdecl SE_RegisterSetupWindowClass() {
WNDCLASSA wndClass;
memset(&wndClass, 0, sizeof(WNDCLASSA));
wndClass.style = CS_HREDRAW|CS_VREDRAW;
wndClass.lpfnWndProc = SE_SetupWindowProc;
wndClass.hInstance = GameModule;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.lpszClassName = DialogClassName;
wndClass.cbWndExtra = sizeof(LONG) * 3;
return ( 0 != RegisterClass(&wndClass) );
}
LRESULT CALLBACK SE_SetupWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
RECT rect;
POINT point;
HDC hdc;
PAINTSTRUCT paint;
char imageName[64];
BITMAP_RESOURCE *pBitmap = (BITMAP_RESOURCE *)GetWindowLong(hWnd, WINLONG_BMP);
switch( uMsg ) {
case WM_CREATE :
GetClientRect(hWnd, &rect);
point.x = rect.right / 2;
point.y = rect.bottom / 2;
MapWindowPoints(hWnd, GetParent(hWnd), &point, 1);
SetWindowLong(hWnd, WINLONG_X, (LONG)point.x);
SetWindowLong(hWnd, WINLONG_Y, (LONG)point.y);
pBitmap = new BITMAP_RESOURCE;
if( pBitmap != NULL ) {
memset(pBitmap, 0, sizeof(BITMAP_RESOURCE));
SetWindowLong(hWnd, WINLONG_BMP, (LONG)pBitmap);
GetWindowText(hWnd, imageName, sizeof(imageName));
SE_CenterBitmapResource(hWnd, pBitmap, imageName);
}
return 0;
case WM_DESTROY :
if( pBitmap != NULL ) {
SE_ReleaseBitmapResource(pBitmap);
delete(pBitmap);
}
break;
case WM_PAINT :
GetClientRect(hWnd, &rect);
hdc = BeginPaint(hWnd, &paint);
DrawEdge(hdc, &rect, BDR_RAISEDINNER|BDR_SUNKENOUTER, BF_ADJUST|BF_BOTTOM|BF_RIGHT|BF_TOP|BF_LEFT);
SE_DrawBitmap(pBitmap, hdc, rect.left, rect.top);
EndPaint(hWnd, &paint);
return 0;
case WM_QUERYNEWPALETTE :
SE_ChangeBitmapPalette(pBitmap, hWnd);
InvalidateRect(hWnd, NULL, FALSE);
UpdateWindow(hWnd);
return 1;
case WM_PALETTECHANGED :
SE_UpdateBitmapPalette(pBitmap, hWnd, (HWND)wParam);
InvalidateRect(hWnd, NULL, FALSE);
UpdateWindow(hWnd);
break;
case WM_CPL_LAUNCH :
SE_CenterBitmapResource(hWnd, pBitmap, (LPCTSTR)wParam);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
void __cdecl SE_PassMessageToImage(HWND hWnd, UINT uMsg, WPARAM wParam) {
if( uMsg == WM_QUERYNEWPALETTE || uMsg == WM_PALETTECHANGED )
SendMessage(hWnd, uMsg, wParam, 0);
}
/*
* Inject function
*/
void Inject_SetupWnd() {
INJECT(0x00456FC0, SE_ReleaseBitmapResource);
INJECT(0x00457000, SE_LoadBitmapResource);
INJECT(0x004570D0, SE_DrawBitmap);
INJECT(0x00457140, SE_UpdateBitmapPalette);
INJECT(0x00457160, SE_ChangeBitmapPalette);
INJECT(0x004571C0, SE_RegisterSetupWindowClass);
INJECT(0x00457230, SE_SetupWindowProc);
INJECT(0x00457470, SE_PassMessageToImage);
}
================================================
FILE: specific/setupwnd.h
================================================
/*
* Copyright (c) 2017 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef SETUPWND_H_INCLUDED
#define SETUPWND_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
void __thiscall SE_ReleaseBitmapResource(BITMAP_RESOURCE *bmpRsrc); // 0x00456FC0
void __thiscall SE_LoadBitmapResource(BITMAP_RESOURCE *bmpRsrc, LPCTSTR lpName); // 0x00457000
void __thiscall SE_DrawBitmap(BITMAP_RESOURCE *bmpRsrc, HDC hdc, int x, int y); // 0x004570D0
void __thiscall SE_UpdateBitmapPalette(BITMAP_RESOURCE *bmpRsrc, HWND hWnd, HWND hSender); // 0x00457140
void __thiscall SE_ChangeBitmapPalette(BITMAP_RESOURCE *bmpRsrc, HWND hWnd); // 0x00457160
bool __cdecl SE_RegisterSetupWindowClass(); // 0x004571C0
LRESULT CALLBACK SE_SetupWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); // 0x00457230
void __cdecl SE_PassMessageToImage(HWND hWnd, UINT uMsg, WPARAM wParam); // 0x00457470
#endif // SETUPWND_H_INCLUDED
================================================
FILE: specific/smain.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/smain.h"
#include "game/cinema.h"
#include "game/demo.h"
#include "game/gameflow.h"
#include "game/inventory.h"
#include "game/invfunc.h"
#include "game/missile.h"
#include "game/savegame.h"
#include "game/setup.h"
#include "game/sound.h"
#include "game/text.h"
#include "specific/display.h"
#include "specific/frontend.h"
#include "specific/game.h"
#include "specific/hwr.h"
#include "specific/init.h"
#include "specific/input.h"
#include "specific/option.h"
#include "specific/output.h"
#include "specific/registry.h"
#include "specific/setupdlg.h"
#include "specific/sndpc.h"
#include "specific/winmain.h"
#include "modding/background_new.h"
#include "global/vars.h"
#ifdef FEATURE_HUD_IMPROVED
extern DWORD DemoTextMode;
extern DWORD JoystickButtonStyle;
extern DWORD SavegameSlots;
extern DWORD InvTextBoxMode;
extern DWORD HealthBarMode;
extern bool PsxBarPosEnabled;
extern bool JoystickHintsEnabled;
extern double GameGUI_Scale;
extern double InvGUI_Scale;
#endif // FEATURE_HUD_IMPROVED
#ifdef FEATURE_BACKGROUND_IMPROVED
static char PictureSuffix[32];
extern DWORD InvBackgroundMode;
extern DWORD StatsBackgroundMode;
extern DWORD PauseBackgroundMode;
extern DWORD PictureStretchLimit;
extern bool LoadingScreensEnabled;
extern bool RemasteredPixEnabled;
#endif // FEATURE_BACKGROUND_IMPROVED
#ifdef FEATURE_VIDEOFX_IMPROVED
extern DWORD ShadowMode;
extern DWORD AlphaBlendMode;
extern DWORD ReflectionMode;
extern DWORD PickupItemMode;
extern bool CustomWaterColorEnabled;
#endif // FEATURE_VIDEOFX_IMPROVED
#ifdef FEATURE_SCREENSHOT_IMPROVED
extern DWORD ScreenshotFormat;
extern char ScreenshotPath[MAX_PATH];
#endif // FEATURE_SCREENSHOT_IMPROVED
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/joy_output.h"
extern bool WalkToSidestep;
extern bool JoystickVibrationEnabled;
extern bool JoystickLedColorEnabled;
#endif // FEATURE_INPUT_IMPROVED
#ifdef FEATURE_MOD_CONFIG
extern bool BarefootSfxEnabled;
#endif // FEATURE_MOD_CONFIG
#ifdef FEATURE_AUDIO_IMPROVED
extern double InventoryMusicMute;
extern double UnderwaterMusicMute;
#endif // FEATURE_AUDIO_IMPROVED
#ifdef FEATURE_VIEW_IMPROVED
extern bool PsxFovEnabled;
extern double ViewDistanceFactor;
extern double FogBeginFactor;
extern double FogEndFactor;
extern double WaterFogBeginFactor;
extern double WaterFogEndFactor;
#endif // FEATURE_VIEW_IMPROVED
#ifdef FEATURE_GAMEPLAY_FIXES
extern bool IsRunningM16fix;
extern bool IsLowCeilingJumpFix;
#endif // FEATURE_GAMEPLAY_FIXES
#ifdef FEATURE_GOLD
extern bool IsGold();
#endif
#ifdef FEATURE_ASSAULT_SAVE
void SaveAssault() {
OpenGameRegistryKey(REG_GAME_KEY);
SetRegistryBinaryValue(REG_GAME_ASSAULT, (LPBYTE)&Assault, sizeof(ASSAULT_STATS));
CloseGameRegistryKey();
}
#endif // FEATURE_ASSAULT_SAVE
BOOL __cdecl GameMain() {
__int16 gfOption, gfDirection, gfParameter;
bool isFrontendFail, isLoopContinue, bonusFlag;
HiRes = 0;
ScreenSizer = 1.0;
GameSizer = 1.0;
if( !S_InitialiseSystem() )
return FALSE;
LPCTSTR scriptFileName = "data\\tombPC.dat";
#ifdef FEATURE_GOLD
if( IsGold() ) {
scriptFileName = "data\\tombPCg.dat";
}
#endif // FEATURE_GOLD
if ( !GF_LoadScriptFile(scriptFileName) ) {
S_ExitSystem("GameMain: could not load script file");
return FALSE; // the app is terminated here
}
SOUND_Init();
InitialiseStartInfo();
// NOTE: S_FrontEndCheck() called before S_LoadSettings() in the original game
S_LoadSettings();
S_FrontEndCheck();
HiRes = -1;
// NOTE: this HWR init was absent in the original code, but must be done here
if( SavedAppSettings.RenderMode == RM_Hardware ) {
HWR_InitState();
}
GameMemoryPointer = (BYTE *)GlobalAlloc(GMEM_FIXED, GameMemorySize);
if( GameMemoryPointer == NULL ) {
lstrcpy(StringToShow, "GameMain: could not allocate malloc_buffer");
return FALSE;
}
HiRes = 0;
TempVideoAdjust(1, 1.0);
S_UpdateInput();
IsVidModeLock = true;
#ifdef FEATURE_BACKGROUND_IMPROVED
int res = -1;
if( *PictureSuffix ) {
char fname[256];
snprintf(fname, sizeof(fname), "data\\legal%s.pcx", PictureSuffix);
res = BGND2_LoadPicture(fname, FALSE, FALSE);
}
if( res ) {
res = BGND2_LoadPicture("data\\legal.pcx", FALSE, FALSE);
}
if( !res ) {
BGND2_ShowPicture(30, 90, 10, 2, TRUE);
}
#else // FEATURE_BACKGROUND_IMPROVED
#ifdef FEATURE_GOLD
S_DisplayPicture(IsGold()?"data\\legalg.pcx":"data\\legal.pcx", FALSE);
#else // !FEATURE_GOLD
S_DisplayPicture("data\\legal.pcx", FALSE);
#endif // !FEATURE_GOLD
S_InitialisePolyList(FALSE);
S_CopyBufferToScreen();
S_OutputPolyList();
S_DumpScreen();
FadeToPal(30, GamePalette8); // fade in 30 frames / 1.0 second (software renderer only)
S_Wait(90 * TICKS_PER_FRAME, TRUE); // wait 90 frames / 3.0 seconds (enable keyboard)
S_FadeToBlack(); // fade out 12 frames / 0.4 second (software renderer only)
#endif // FEATURE_BACKGROUND_IMPROVED
S_DontDisplayPicture();
IsVidModeLock = false;
isFrontendFail = GF_DoFrontEndSequence();
if( IsGameToExit ) {
return TRUE;
}
if( isFrontendFail ) {
lstrcpy(StringToShow, "GameMain: failed in GF_DoFrontEndSequence()");
return FALSE;
}
S_FadeToBlack();
IsTitleLoaded = FALSE;
gfOption = GF_GameFlow.firstOption;
isLoopContinue = true;
while( isLoopContinue ) {
gfDirection = gfOption & 0xFF00;
gfParameter = gfOption & 0x00FF;
switch( gfDirection ) {
case GF_START_GAME :
if( GF_GameFlow.singleLevel >= 0 ) {
gfOption = GF_DoLevelSequence(GF_GameFlow.singleLevel, GFL_NORMAL);
} else {
if( gfParameter > GF_GameFlow.num_Levels ) {
wsprintf(StringToShow, "GameMain: STARTGAME with invalid level number (%d)", gfParameter);
return FALSE;
}
gfOption = GF_DoLevelSequence(gfParameter, GFL_NORMAL);
}
break;
case GF_START_SAVEDGAME :
S_LoadGame(&SaveGame, sizeof(SAVEGAME_INFO), gfParameter);
if( SaveGame.currentLevel > GF_GameFlow.num_Levels ) {
wsprintf(StringToShow, "GameMain: STARTSAVEDGAME with invalid level number (%d)", SaveGame.currentLevel);
return FALSE;
}
gfOption = GF_DoLevelSequence(SaveGame.currentLevel, GFL_SAVED);
break;
case GF_START_CINE :
StartCinematic(gfParameter);
gfOption = GF_EXIT_TO_TITLE;
break;
case GF_START_DEMO :
// NOTE: there is no such bonus flag reset in the original game
bonusFlag = SaveGame.bonusFlag; // backup bonusFlag
SaveGame.bonusFlag = 0; // remove bonusFlag while Demo is playing
gfOption = DoDemoSequence(-1);
SaveGame.bonusFlag = bonusFlag; // restore bonusFlag
break;
case GF_LEVEL_COMPLETE :
gfOption = LevelCompleteSequence();
break;
case GF_EXIT_TO_TITLE :
case GF_EXIT_TO_OPTION :
if( (GF_GameFlow.flags & GFF_TitleDisabled) != 0 ) {
gfOption = GF_GameFlow.titleReplace;
if( gfOption == GF_EXIT_TO_TITLE || gfOption < 0 ) {
lstrcpy(StringToShow, "GameMain Failed: Title disabled & no replacement");
return FALSE;
}
} else {
gfOption = TitleSequence();
GF_StartGame = true;
}
break;
case GF_START_FMV :
case GF_EXIT_GAME :
default :
isLoopContinue = false;
break;
}
}
S_SaveSettings();
ShutdownGame();
return TRUE;
}
__int16 __cdecl TitleSequence() {
T_InitPrint();
TempVideoAdjust(1, 1.0);
NoInputCounter = 0;
if( !IsTitleLoaded ) {
if( !InitialiseLevel(0, 0) )
return GF_EXIT_GAME;
IsTitleLoaded = TRUE;
}
#ifdef FEATURE_BACKGROUND_IMPROVED
int res = -1;
if( *PictureSuffix ) {
char fname[256];
snprintf(fname, sizeof(fname), "data\\title%s.pcx", PictureSuffix);
res = BGND2_LoadPicture(fname, TRUE, FALSE);
}
if( res ) {
res = BGND2_LoadPicture("data\\title.pcx", TRUE, FALSE);
}
if( !res ) {
// NOTE: title menu fade-in was absent in the original game
BGND2_ShowPicture(15, 0, 0, 0, FALSE);
}
#elif defined(FEATURE_GOLD)
S_DisplayPicture(IsGold()?"data\\titleg.pcx":"data\\title.pcx", TRUE);
#else // FEATURE_BACKGROUND_IMPROVED
S_DisplayPicture("data\\title.pcx", TRUE);
#endif // FEATURE_BACKGROUND_IMPROVED
if( GF_GameFlow.titleTrack != 0 )
S_CDPlay(GF_GameFlow.titleTrack, TRUE);
Display_Inventory(INV_TitleMode);
#ifdef FEATURE_BACKGROUND_IMPROVED
BGND2_ShowPicture(0, 0, 10, 2, FALSE);
#else // FEATURE_BACKGROUND_IMPROVED
S_FadeToBlack();
#endif // FEATURE_BACKGROUND_IMPROVED
S_DontDisplayPicture();
S_CDStop();
if( IsResetFlag ) {
IsResetFlag = FALSE;
return GF_START_DEMO;
}
if( InventoryChosen == ID_PHOTO_OPTION )
return GF_START_GAME | 0;
if( InventoryChosen == ID_PASSPORT_OPTION ) {
if ( InventoryExtraData[0] == 0 ) {
__int16 slotNumber = InventoryExtraData[1];
Inv_RemoveAllItems();
S_LoadGame(&SaveGame, sizeof(SAVEGAME_INFO), slotNumber);
return GF_START_SAVEDGAME | slotNumber;
}
if( InventoryExtraData[0] == 1 ) {
__int16 levelID = 1;
InitialiseStartInfo();
if( (GF_GameFlow.flags & GFF_SelectAnyLevel) != 0 )
levelID = InventoryExtraData[1] + 1;
return GF_START_GAME | levelID;
}
}
return GF_EXIT_GAME;
}
void __cdecl CheckCheatMode() {
static int mode = 0;
static int turn = 0;
static __int16 angle = 0;
static bool isFlare = false;
__int16 as = LaraItem->currentAnimState;
// Cheat is disabled in Lara home and final level
if( CurrentLevel == 0 || CurrentLevel == GF_GameFlow.num_Levels-GF_GameFlow.num_Demos-1 )
return;
switch( mode ) {
case 0:
// Any State -> Step forward
if( as == AS_WALK )
mode = 1;
break;
case 1 :
// Check flare
isFlare = ( Lara.gun_type == LGT_Flare );
// Step forward -> Stop
if( as != AS_WALK )
mode = ( as == AS_STOP ) ? 2 : 0;
break;
case 2 :
// Stop -> Step back
if( as != AS_STOP )
mode = ( as == AS_BACK ) ? 3 : 0;
break;
case 3 :
// Step back -> Stop
if( as != AS_BACK )
mode = ( as == AS_STOP ) ? 4 : 0;
break;
case 4 :
// Stop -> Start turn left / right
if( as != AS_STOP ) {
turn = 0;
angle = LaraItem->pos.rotY;
mode = ( as == AS_TURN_L ) ? 5 : ( as == AS_TURN_R ) ? 6 : 0;
}
break;
case 5 :
// Continue turn left (required at least 518 degrees / 1.44 spins)
if( as == AS_TURN_L || as == AS_FASTTURN ) {
turn += (__int16)(LaraItem->pos.rotY - angle);
angle = LaraItem->pos.rotY;
} else {
mode = ( turn < -0x17000 ) ? 7 : 0;
}
break;
case 6 :
// Continue turn right (required at least 518 degrees / 1.44 spins)
if ( as == AS_TURN_R || as == AS_FASTTURN ) {
turn += (__int16)(LaraItem->pos.rotY - angle);
angle = LaraItem->pos.rotY;
} else {
mode = ( turn > 0x17000 ) ? 7 : 0;
}
break;
case 7 :
// Stop -> Start jump
if( as != AS_STOP )
mode = ( as == AS_COMPRESS ) ? 8 : 0;
break;
case 8 :
// Start falling
if( LaraItem->fallSpeed > 0 ) {
// Check if jump interrupted
if( as != AS_FORWARDJUMP && as != AS_BACKJUMP ) {
// Finish cheat sequence with no action
} // Check if flare is not active
else if( !isFlare || Lara.gun_type != LGT_Flare ) {
// Explode Lara!
ExplodingDeath(Lara.item_number, 0xFFFFFFFF, 1);
LaraItem->hitPoints = 0;
LaraItem->flags |= IFL_INVISIBLE;
#ifdef FEATURE_INPUT_IMPROVED
JoyRumbleExplode(LaraItem->pos.x, LaraItem->pos.y, LaraItem->pos.z, 0x1400, true);
#endif // FEATURE_INPUT_IMPROVED
} // Check jump forward
else if( as == AS_FORWARDJUMP ) {
// Complete level
IsLevelComplete = TRUE;
} // Check jump backward
else if( as == AS_BACKJUMP ) {
// Give weapon and ammo
// NOTE: additional weapon availability checks not presented in the original game
if( Objects[ID_SHOTGUN_OPTION].loaded ) {
if( !Inv_RequestItem(ID_SHOTGUN_ITEM) ) {
Inv_AddItem(ID_SHOTGUN_ITEM);
}
Lara.shotgun_ammo = SaveGame.bonusFlag ? 10001 : 500;
}
if( Objects[ID_MAGNUM_OPTION].loaded ) {
if( !Inv_RequestItem(ID_MAGNUM_ITEM) ) {
Inv_AddItem(ID_MAGNUM_ITEM);
}
Lara.magnum_ammo = SaveGame.bonusFlag ? 10001 : 500;
}
if( Objects[ID_UZI_OPTION].loaded ) {
if( !Inv_RequestItem(ID_UZI_ITEM) ) {
Inv_AddItem(ID_UZI_ITEM);
}
Lara.uzi_ammo = SaveGame.bonusFlag ? 10001 : 5000;
}
if( Objects[ID_HARPOON_OPTION].loaded ) {
if( !Inv_RequestItem(ID_HARPOON_ITEM) ) {
Inv_AddItem(ID_HARPOON_ITEM);
}
Lara.harpoon_ammo = SaveGame.bonusFlag ? 10001 : 5000;
}
if( Objects[ID_M16_OPTION].loaded ) {
if( !Inv_RequestItem(ID_M16_ITEM) ) {
Inv_AddItem(ID_M16_ITEM);
}
Lara.m16_ammo = SaveGame.bonusFlag ? 10001 : 5000;
}
if( Objects[ID_GRENADE_OPTION].loaded ) {
if( !Inv_RequestItem(ID_GRENADE_ITEM) ) {
Inv_AddItem(ID_GRENADE_ITEM);
}
Lara.grenade_ammo = SaveGame.bonusFlag ? 10001 : 5000;
}
// Give medipacks and flares
for( int i=0; i<50; ++i ) {
// NOTE: there are no limits in the original code, but it works wrong without limits
if( Objects[ID_SMALL_MEDIPACK_OPTION].loaded && Inv_RequestItem(ID_SMALL_MEDIPACK_ITEM) < 240 ) {
Inv_AddItem(ID_SMALL_MEDIPACK_ITEM);
}
if( Objects[ID_LARGE_MEDIPACK_OPTION].loaded && Inv_RequestItem(ID_LARGE_MEDIPACK_ITEM) < 240 ) {
Inv_AddItem(ID_LARGE_MEDIPACK_ITEM);
}
if( Objects[ID_FLARES_OPTION].loaded && Inv_RequestItem(ID_FLARE_ITEM) < 240 ) {
Inv_AddItem(ID_FLARE_ITEM);
}
}
// Play SFX
PlaySoundEffect(7, NULL, SFX_ALWAYS);
}
mode = 0;
}
break;
default :
mode = 0;
break;
}
}
void __cdecl S_SaveSettings() {
OpenGameRegistryKey(REG_GAME_KEY);
SetRegistryDwordValue(REG_MUSIC_VOLUME, MusicVolume);
SetRegistryDwordValue(REG_SOUND_VOLUME, SoundVolume);
SetRegistryDwordValue(REG_DETAIL_LEVEL, DetailLevel);
#ifndef FEATURE_NOLEGACY_OPTIONS
SetRegistryFloatValue(REG_GAME_SIZER, GameSizer);
#endif // FEATURE_NOLEGACY_OPTIONS
#ifdef FEATURE_HUD_IMPROVED
SetRegistryBinaryValue(REG_GAME_JOY_LAYOUT, Layout[CTRL_Joystick].key, sizeof(CONTROL_LAYOUT));
SetRegistryBinaryValue(REG_GAME_KBD_LAYOUT, Layout[CTRL_Custom].key, sizeof(CONTROL_LAYOUT));
SetRegistryBoolValue(REG_JOYSTICK_HINTS, JoystickHintsEnabled);
#else // FEATURE_HUD_IMPROVED
SetRegistryBinaryValue(REG_GAME_LAYOUT, (LPBYTE)Layout[CTRL_Custom].key, sizeof(CONTROL_LAYOUT));
#endif // FEATURE_HUD_IMPROVED
#ifdef FEATURE_INPUT_IMPROVED
SetRegistryBoolValue(REG_JOYSTICK_VIBRATION, JoystickVibrationEnabled);
#endif // FEATURE_INPUT_IMPROVED
CloseGameRegistryKey();
#ifdef FEATURE_VIEW_IMPROVED
OpenGameRegistryKey(REG_VIEW_KEY);
SetRegistryFloatValue(REG_DRAW_DISTANCE, ViewDistanceFactor);
SetRegistryFloatValue(REG_FOG_BEGIN, FogBeginFactor);
SetRegistryFloatValue(REG_FOG_END, FogEndFactor);
SetRegistryFloatValue(REG_UW_FOG_BEGIN, WaterFogBeginFactor);
SetRegistryFloatValue(REG_UW_FOG_END, WaterFogEndFactor);
CloseGameRegistryKey();
#endif // FEATURE_VIEW_IMPROVED
}
void __cdecl S_LoadSettings() {
DWORD soundVol = 0;
DWORD musicVol = 0;
OpenGameRegistryKey(REG_GAME_KEY);
GetRegistryDwordValue(REG_MUSIC_VOLUME, &musicVol, 10); // NOTE: There was bug in the original code. 165 instead of 10.
GetRegistryDwordValue(REG_SOUND_VOLUME, &soundVol, 10);
GetRegistryDwordValue(REG_DETAIL_LEVEL, &DetailLevel, 1);
#ifdef FEATURE_NOLEGACY_OPTIONS
GameSizer = 1.0;
#else // FEATURE_NOLEGACY_OPTIONS
GetRegistryFloatValue(REG_GAME_SIZER, &GameSizer, 1.0);
#endif // FEATURE_NOLEGACY_OPTIONS
#ifdef FEATURE_HUD_IMPROVED
GetRegistryBinaryValue(REG_GAME_JOY_LAYOUT, Layout[CTRL_Joystick].key, sizeof(CONTROL_LAYOUT), NULL);
if( !GetRegistryBinaryValue(REG_GAME_KBD_LAYOUT, Layout[CTRL_Custom].key, sizeof(CONTROL_LAYOUT), NULL) ) {
UINT16 legacy[14];
if( GetRegistryBinaryValue(REG_GAME_LAYOUT, (LPBYTE)legacy, sizeof(legacy), NULL) ) {
// migrate the legacy keyboard Layout to the new one
UINT16 remap[14] = {0,1,2,3,4,5, 7,8,9, 11,12,13, 10,14};
for( int i=0; i<14; ++i ) {
if( legacy[i] < 0x100 ) {
Layout[CTRL_Custom].key[remap[i]] = legacy[i];
}
}
}
}
GetRegistryDwordValue(REG_DEMOTEXT_MODE, &DemoTextMode, 0);
GetRegistryDwordValue(REG_JOYSTICK_BTN_STYLE, &JoystickButtonStyle, 0);
GetRegistryDwordValue(REG_SAVEGAME_SLOTS, &SavegameSlots, 0);
GetRegistryDwordValue(REG_INVTEXTBOX_MODE, &InvTextBoxMode, 1);
GetRegistryDwordValue(REG_HEALTHBAR_MODE, &HealthBarMode, 2);
GetRegistryBoolValue(REG_PSXBARPOS_ENABLE, &PsxBarPosEnabled, true);
GetRegistryBoolValue(REG_JOYSTICK_HINTS, &JoystickHintsEnabled, true);
GetRegistryFloatValue(REG_GAME_GUI_SCALE, &GameGUI_Scale, 1.0);
GetRegistryFloatValue(REG_INV_GUI_SCALE, &InvGUI_Scale, 1.0);
if( JoystickButtonStyle > 3 ) {
JoystickButtonStyle = 0;
}
CLAMP(SavegameSlots, 16, 24);
CLAMP(GameGUI_Scale, 0.5, 2.0);
CLAMP(InvGUI_Scale, 0.5, 2.0);
#else // FEATURE_HUD_IMPROVED
GetRegistryBinaryValue(REG_GAME_LAYOUT, (LPBYTE)Layout[CTRL_Custom].key, sizeof(CONTROL_LAYOUT), NULL);
#endif // FEATURE_HUD_IMPROVED
#ifdef FEATURE_INPUT_IMPROVED
GetRegistryBoolValue(REG_WALK_TO_SIDESTEP, &WalkToSidestep, false);
GetRegistryBoolValue(REG_JOYSTICK_VIBRATION, &JoystickVibrationEnabled, true);
GetRegistryBoolValue(REG_JOYSTICK_LED_COLOR, &JoystickLedColorEnabled, true);
#endif // FEATURE_INPUT_IMPROVED
#ifdef FEATURE_BACKGROUND_IMPROVED
GetRegistryDwordValue(REG_INVBGND_MODE, &InvBackgroundMode, 2);
GetRegistryDwordValue(REG_STATSBGND_MODE, &StatsBackgroundMode, 0);
GetRegistryDwordValue(REG_PAUSEBGND_MODE, &PauseBackgroundMode, 1);
GetRegistryDwordValue(REG_PICTURE_STRETCH, &PictureStretchLimit, 10);
GetRegistryBoolValue(REG_REMASTER_PIX_ENABLE, &RemasteredPixEnabled, true);
GetRegistryBoolValue(REG_LOADING_SCREENS, &LoadingScreensEnabled, true);
GetRegistryStringValue(REG_PICTURE_SUFFIX, PictureSuffix, sizeof(PictureSuffix), "");
#endif // FEATURE_BACKGROUND_IMPROVED
#ifdef FEATURE_VIDEOFX_IMPROVED
GetRegistryDwordValue(REG_SHADOW_MODE, &ShadowMode, 1);
GetRegistryDwordValue(REG_ALPHABLEND_MODE, &AlphaBlendMode, 2);
GetRegistryDwordValue(REG_REFLECTION_MODE, &ReflectionMode, 2);
GetRegistryDwordValue(REG_PICKUPITEM_MODE, &PickupItemMode, 1);
GetRegistryBoolValue(REG_CUSTOM_WATER_COLOR, &CustomWaterColorEnabled, true);
CLAMPG(AlphaBlendMode, 2);
CLAMPG(ReflectionMode, 3);
#endif // FEATURE_VIDEOFX_IMPROVED
#ifdef FEATURE_SCREENSHOT_IMPROVED
GetRegistryDwordValue(REG_SCREENSHOT_FORMAT, &ScreenshotFormat, 1);
GetRegistryStringValue(REG_SCREENSHOT_PATH, ScreenshotPath, sizeof(ScreenshotPath), ".\\screenshots");
#endif // FEATURE_SCREENSHOT_IMPROVED
#ifdef FEATURE_ASSAULT_SAVE
GetRegistryBinaryValue(REG_GAME_ASSAULT, (LPBYTE)&Assault, sizeof(Assault), NULL);
if( Assault.bestTime[0] > 0 ) {
AssaultBestTime = Assault.bestTime[0];
}
#endif // FEATURE_ASSAULT_SAVE
#ifdef FEATURE_AUDIO_IMPROVED
GetRegistryFloatValue(REG_INV_MUSIC_MUTE, &InventoryMusicMute, 0.8);
GetRegistryFloatValue(REG_UW_MUSIC_MUTE, &UnderwaterMusicMute, 0.8);
CLAMP(InventoryMusicMute, 0.0, 1.0);
CLAMP(UnderwaterMusicMute, 0.0, 1.0);
#endif // FEATURE_AUDIO_IMPROVED
#ifdef FEATURE_VIEW_IMPROVED
GetRegistryBoolValue(REG_PSXFOV_ENABLE, &PsxFovEnabled, false);
#endif // FEATURE_VIEW_IMPROVED
#ifdef FEATURE_MOD_CONFIG
GetRegistryBoolValue(REG_BAREFOOT_SFX_ENABLE, &BarefootSfxEnabled, true);
#endif // FEATURE_MOD_CONFIG
#ifdef FEATURE_GOLD
if( IsGold() ) {
// This RJF check is presented in "The Golden Mask" only
DWORD rjf = 0;
GetRegistryDwordValue("RJF", &rjf, 0);
if( rjf == 150868 ) {
GF_GameFlow.flags |= GFF_SelectAnyLevel;
} else {
DeleteRegistryValue("RJF");
}
}
#endif // FEATURE_GOLD
CloseGameRegistryKey();
// NOTE: There was no such call in the original code, which produces control configuration bugs
DefaultConflict();
// NOTE: There was no such volume range check in the original game
SoundVolume = (soundVol > 10) ? 10 : soundVol;
MusicVolume = (musicVol > 10) ? 10 : musicVol;
S_SoundSetMasterVolume(6 * SoundVolume + 4); // 4, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64
S_CDVolume(MusicVolume ? MusicVolume*25+5 : 0); // 0, 30, 55, 80, 105, 130, 155, 180, 205, 230, 255
#ifdef FEATURE_GAMEPLAY_FIXES
OpenGameRegistryKey(REG_BUGS_KEY);
GetRegistryBoolValue(REG_RUNNING_M16_FIX, &IsRunningM16fix, false);
GetRegistryBoolValue(REG_LOWCEILING_JUMP_FIX, &IsLowCeilingJumpFix, true);
CloseGameRegistryKey();
#endif // FEATURE_GAMEPLAY_FIXES
#ifdef FEATURE_VIEW_IMPROVED
OpenGameRegistryKey(REG_VIEW_KEY);
GetRegistryFloatValue(REG_DRAW_DISTANCE, &ViewDistanceFactor, 6.0);
GetRegistryFloatValue(REG_FOG_BEGIN, &FogBeginFactor, 1.0);
GetRegistryFloatValue(REG_FOG_END, &FogEndFactor, 6.0);
GetRegistryFloatValue(REG_UW_FOG_BEGIN, &WaterFogBeginFactor, 0.6);
GetRegistryFloatValue(REG_UW_FOG_END, &WaterFogEndFactor, 1.0);
CloseGameRegistryKey();
CLAMP(ViewDistanceFactor, 1.0, 6.0);
CLAMP(FogEndFactor, 0.0, ViewDistanceFactor);
CLAMP(FogBeginFactor, 0.0, FogEndFactor);
CLAMP(WaterFogEndFactor, 0.0, FogEndFactor);
CLAMP(WaterFogBeginFactor, 0.0, FogBeginFactor);
setup_screen_size();
#endif // FEATURE_VIEW_IMPROVED
}
// NOTE: this function is presented in the "Golden Mask" only
void __cdecl EnableLevelSelect() {
#ifdef FEATURE_GOLD
if( IsGold() ) {
OpenGameRegistryKey(REG_GAME_KEY);
// NOTE: It seems that RJF=150868 refers to
// Richard J. Flower ("The Golden Mask" programmer)
// and probably his birthday on August 15th, 1968.
SetRegistryDwordValue("RJF", 150868);
GF_GameFlow.flags |= GFF_SelectAnyLevel;
CloseGameRegistryKey();
}
#endif // FEATURE_GOLD
}
/*
* Inject function
*/
void Inject_SMain() {
INJECT(0x00454B10, GameMain);
INJECT(0x00454DE0, TitleSequence);
INJECT(0x00454EF0, CheckCheatMode);
INJECT(0x00455250, S_SaveSettings);
INJECT(0x004552D0, S_LoadSettings);
}
================================================
FILE: specific/smain.h
================================================
/*
* Copyright (c) 2017-2019 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef SMAIN_H_INCLUDED
#define SMAIN_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
BOOL __cdecl GameMain(); // 0x00454B10
__int16 __cdecl TitleSequence(); // 0x00454DE0
void __cdecl CheckCheatMode(); // 0x00454EF0
void __cdecl S_SaveSettings(); // 0x00455250
void __cdecl S_LoadSettings(); // 0x004552D0
void __cdecl EnableLevelSelect(); // tomb2gold:0x00455960
#endif // SMAIN_H_INCLUDED
================================================
FILE: specific/sndpc.cpp
================================================
/*
* Copyright (c) 2017 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/sndpc.h"
#include "game/sound.h"
#include "specific/file.h"
#include "specific/init_sound.h"
#include "specific/utils.h"
#include "global/resource.h"
#include "global/vars.h"
#ifdef FEATURE_AUDIO_IMPROVED
double InventoryMusicMute = 0.8;
double UnderwaterMusicMute = 0.8;
#endif // FEATURE_AUDIO_IMPROVED
#ifdef FEATURE_PAULD_CDAUDIO
#include "modding/cd_pauld.h"
static bool PaulD_isActive = false;
#endif // FEATURE_PAULD_CDAUDIO
// NOTE: There is no such flag in the original game.
// It is added to provide additional protection against crashes
static bool isCDAudioEnabled = false;
// NOTE: this variable is absent in the original game
static DWORD CDVolume = 0;
int __cdecl S_SoundPlaySample(int channel, UINT16 volume, int pitch, int pan) {
if( !SoundIsActive )
return -3;
int calcPan = S_Sound_CalculateSamplePan(pan);
int calcVolume = S_Sound_CalculateSampleVolume(volume);
return WinSndPlaySample(channel, calcVolume, pitch, calcPan, 0);
}
int __cdecl S_Sound_CalculateSampleVolume(DWORD volume) {
return (int)(((double)(S_MasterVolume * volume)/0x200000.p0 - 1.0) * 5000.0);
}
int __cdecl S_Sound_CalculateSamplePan(__int16 pan) {
return pan/16;
}
int __cdecl S_SoundPlaySampleLooped(int channel, UINT16 volume, DWORD pitch, int pan) {
if( !SoundIsActive )
return -3;
int calcPan = S_Sound_CalculateSamplePan(pan);
int calcVolume = S_Sound_CalculateSampleVolume(volume);
return WinSndPlaySample(channel, calcVolume, pitch, calcPan, DSBPLAY_LOOPING);
}
void __cdecl S_SoundSetPanAndVolume(int channel, int pan, UINT16 volume) {
if( SoundIsActive ) {
int calcPan = S_Sound_CalculateSamplePan(pan);
int calcVolume = S_Sound_CalculateSampleVolume(volume);
WinSndAdjustVolumeAndPan(channel, calcVolume, calcPan);
}
}
void __cdecl S_SoundSetPitch(int channel, DWORD pitch) {
if( SoundIsActive )
WinSndAdjustPitch(channel, pitch);
}
void __cdecl S_SoundSetMasterVolume(DWORD volume) {
S_MasterVolume = volume;
}
void __cdecl S_SoundStopSample(int channel) {
if( SoundIsActive )
WinSndStopSample(channel);
}
void __cdecl S_SoundStopAllSamples() {
if( SoundIsActive )
for( DWORD i=0; i<32; ++i )
WinSndStopSample(i);
}
BOOL __cdecl S_SoundSampleIsPlaying(int channel) {
if( !SoundIsActive )
return FALSE;
return WinSndIsChannelPlaying(channel);
}
bool __cdecl CD_Init() {
MCI_OPEN_PARMS openParams;
MCI_SET_PARMS setParams;
#ifndef FEATURE_NOCD_DATA
while( !SelectDrive() ) {
if( !CD_NoteAlert(MAKEINTRESOURCE(IDD_CD_PROMPT), HGameWindow) )
return false;
}
#endif // !FEATURE_NOCD_DATA
#ifdef FEATURE_PAULD_CDAUDIO
PaulD_isActive = PaulD_CD_Init();
if( PaulD_isActive ) return true;
#endif // FEATURE_PAULD_CDAUDIO
if( isCDAudioEnabled )
return true;
openParams.lpstrDeviceType = "cdaudio";
if( 0 == mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE, (DWORD_PTR)&openParams) ) {
MciDeviceID = openParams.wDeviceID;
setParams.dwTimeFormat = MCI_FORMAT_TMSF;
mciSendCommand(MciDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&setParams);
isCDAudioEnabled = true;
}
return true;
}
void __cdecl CD_Cleanup() {
MCI_GENERIC_PARMS params;
#ifdef FEATURE_PAULD_CDAUDIO
if( PaulD_isActive ) {
PaulD_CD_Cleanup();
return;
}
#endif // FEATURE_PAULD_CDAUDIO
if( !isCDAudioEnabled )
return;
mciSendCommand(MciDeviceID, MCI_STOP, 0, (DWORD_PTR)¶ms);
mciSendCommand(MciDeviceID, MCI_CLOSE, 0, (DWORD_PTR)¶ms);
isCDAudioEnabled = false;
}
void __cdecl S_CDLoop() {
int rc;
MCI_PLAY_PARMS playParams;
MCI_STATUS_PARMS statusParams;
#ifdef FEATURE_PAULD_CDAUDIO
if( PaulD_isActive ) {
PaulD_CDLoop();
return;
}
#endif // FEATURE_PAULD_CDAUDIO
if( CD_LoopTrack == 0 || ++CD_LoopCounter < 150 )
return;
CD_LoopCounter = 0;
statusParams.dwItem = MCI_STATUS_MODE;
rc = mciSendCommand(MciDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&statusParams);
if( (rc == 0) && (statusParams.dwReturn == MCI_MODE_STOP) ) {
playParams.dwFrom = CD_LoopTrack;
playParams.dwTo = CD_LoopTrack + 1;
mciSendCommand(MciDeviceID, MCI_PLAY, MCI_NOTIFY_FAILURE|MCI_NOTIFY_ABORTED, (DWORD_PTR)&playParams);
}
}
void __cdecl S_CDPlay(__int16 trackID, BOOL isLooped) {
__int16 track;
MCI_PLAY_PARMS playParams;
#ifdef FEATURE_PAULD_CDAUDIO
if( PaulD_isActive ) {
PaulD_CDPlay(trackID, isLooped);
return;
}
#endif // FEATURE_PAULD_CDAUDIO
if( MusicVolume == 0 )
return;
CD_TrackID = trackID;
track = GetRealTrack(trackID);
playParams.dwFrom = track;
playParams.dwTo = track + 1;
mciSendCommand(MciDeviceID, MCI_PLAY, MCI_NOTIFY_FAILURE|MCI_NOTIFY_ABORTED, (DWORD_PTR)&playParams);
if( isLooped ) {
CD_LoopTrack = track;
CD_LoopCounter = 120;
}
}
void __cdecl S_CDStop() {
MCI_GENERIC_PARMS params;
#ifdef FEATURE_PAULD_CDAUDIO
if( PaulD_isActive ) {
PaulD_CDStop();
return;
}
#endif // FEATURE_PAULD_CDAUDIO
if( CD_TrackID > 0 ) {
mciSendCommand(MciDeviceID, MCI_STOP, 0, (DWORD_PTR)¶ms);
CD_TrackID = 0;
CD_LoopTrack = 0;
}
}
BOOL __cdecl StartSyncedAudio(int trackID) {
__int16 track;
MCI_PLAY_PARMS playParams;
MCI_SET_PARMS setParams;
#ifdef FEATURE_PAULD_CDAUDIO
if( PaulD_isActive )
return PaulD_StartSyncedAudio(trackID);
#endif // FEATURE_PAULD_CDAUDIO
CD_TrackID = trackID;
track = GetRealTrack(trackID);
setParams.dwTimeFormat = MCI_FORMAT_TMSF;
if( 0 != mciSendCommand(MciDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&setParams) )
return FALSE;
playParams.dwFrom = track;
playParams.dwTo = track + 1;
return ( 0 == mciSendCommand(MciDeviceID, MCI_PLAY, MCI_NOTIFY_FAILURE|MCI_NOTIFY_ABORTED, (DWORD_PTR)&playParams) );
}
DWORD __cdecl S_CDGetLoc() {
DWORD pos;
MCI_STATUS_PARMS statusParams;
#ifdef FEATURE_PAULD_CDAUDIO
if( PaulD_isActive )
return PaulD_CDGetLoc();
#endif // FEATURE_PAULD_CDAUDIO
statusParams.dwItem = MCI_STATUS_POSITION;
if( 0 != mciSendCommand(MciDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&statusParams) )
return 0;
pos = statusParams.dwReturn;
// calculate audio frames position (75 audio frames per second)
return (MCI_TMSF_MINUTE(pos)*60 + MCI_TMSF_SECOND(pos))*75 + MCI_TMSF_FRAME(pos);
}
void __cdecl S_CDVolume(DWORD volume) {
AUXCAPS caps;
bool isVolumeSet = false;
UINT deviceID = (UINT)(-1);
UINT auxDevCount = auxGetNumDevs();
CDVolume = volume; // NOTE: store current CD Audio volume
#ifdef FEATURE_PAULD_CDAUDIO
if( PaulD_isActive ) {
PaulD_CDVolume(volume);
return;
}
#endif // FEATURE_PAULD_CDAUDIO
if( auxDevCount == 0)
return;
volume *= 0x100; // 0 .. 255 -> 0..65280
for( UINT i=0; i .
*/
#ifndef SNDPC_H_INCLUDED
#define SNDPC_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
int __cdecl S_SoundPlaySample(int channel, UINT16 volume, int pitch, int pan); // 0x004553B0
int __cdecl S_Sound_CalculateSampleVolume(DWORD volume); // 0x00455400
int __cdecl S_Sound_CalculateSamplePan(__int16 pan); // 0x00455430
int __cdecl S_SoundPlaySampleLooped(int channel, UINT16 volume, DWORD pitch, int pan); // 0x00455460
void __cdecl S_SoundSetPanAndVolume(int channel, int pan, UINT16 volume); // 0x004554B0
void __cdecl S_SoundSetPitch(int channel, DWORD pitch); // 0x004554F0
void __cdecl S_SoundSetMasterVolume(DWORD volume); // 0x00455510
void __cdecl S_SoundStopSample(int channel); // 0x00455520
void __cdecl S_SoundStopAllSamples(); // 0x00455540
BOOL __cdecl S_SoundSampleIsPlaying(int channel); // 0x00455550
bool __cdecl CD_Init(); // 0x00455570
void __cdecl CD_Cleanup(); // 0x00455600
void __cdecl S_CDLoop(); // 0x00455640
void __cdecl S_CDPlay(__int16 trackID, BOOL isLooped); // 0x004556E0
void __cdecl S_CDStop(); // 0x00455760
BOOL __cdecl StartSyncedAudio(int trackID); // 0x004557A0
DWORD __cdecl S_CDGetLoc(); // 0x00455830
void __cdecl S_CDVolume(DWORD volume); // 0x004558A0
DWORD __cdecl S_GetCDVolume(); // NOTE: this function is not presented in the original game
#endif // SNDPC_H_INCLUDED
================================================
FILE: specific/texture.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/texture.h"
#include "3dsystem/3d_gen.h"
#include "specific/hwr.h"
#include "specific/winvid.h"
#include "global/vars.h"
#include
#ifdef FEATURE_BACKGROUND_IMPROVED
#include "modding/background_new.h"
#endif // FEATURE_BACKGROUND_IMPROVED
#ifdef FEATURE_EXTENDED_LIMITS
PHD_TEXTURE PhdTextureInfo[0x2000];
BYTE LabTextureUVFlags[0x2000];
BYTE *TexturePageBuffer8[128];
HWR_TEXHANDLE HWR_PageHandles[128];
int HWR_TexturePageIndexes[128];
#endif // FEATURE_EXTENDED_LIMITS
#if defined(FEATURE_EXTENDED_LIMITS) || defined(FEATURE_BACKGROUND_IMPROVED)
TEXPAGE_DESC TexturePages[256];
#else // defined(FEATURE_EXTENDED_LIMITS) || defined(FEATURE_BACKGROUND_IMPROVED)
TEXPAGE_DESC TexturePages[32];
#endif // defined(FEATURE_EXTENDED_LIMITS) || defined(FEATURE_BACKGROUND_IMPROVED)
#if (DIRECT3D_VERSION >= 0x900)
RGB888 *TexturePalettes[256];
#elif defined(FEATURE_EXTENDED_LIMITS) || defined(FEATURE_BACKGROUND_IMPROVED)
LPDIRECTDRAWPALETTE TexturePalettes[256];
#else // defined(FEATURE_EXTENDED_LIMITS) || defined(FEATURE_BACKGROUND_IMPROVED)
LPDIRECTDRAWPALETTE TexturePalettes[16];
#endif // defined(FEATURE_EXTENDED_LIMITS) || defined(FEATURE_BACKGROUND_IMPROVED)
#ifdef FEATURE_VIDEOFX_IMPROVED
DWORD ReflectionMode = 2;
#if (DIRECT3D_VERSION >= 0x900)
static LPDIRECT3DTEXTURE9 EnvmapTexture = NULL;
#else // (DIRECT3D_VERSION >= 0x900)
extern LPDDS EnvmapBufferSurface;
extern LPDDS CaptureBufferSurface;
static LPDIRECT3DTEXTURE2 EnvmapTexture = NULL;
#endif // (DIRECT3D_VERSION >= 0x900)
static HWR_TEXHANDLE EnvmapTextureHandle = 0;
static DWORD GetEnvmapSide() {
static const DWORD mapside[] = {64, 256, 1024};
if( ReflectionMode < 1 || ReflectionMode > 3 ) return 0;
DWORD side = MIN(mapside[3 - ReflectionMode], GetMaxTextureSize());
DWORD sideLimit = MIN(GameVidWidth, GameVidHeight);
while( side > sideLimit ) side >>= 1;
return side;
}
#if (DIRECT3D_VERSION >= 0x900)
static bool __cdecl CreateEnvmapTexture() {
if( EnvmapTexture != NULL ) FreeEnvmapTexture();
DWORD side = GetEnvmapSide();
if( !side ) return false;
if( EnvmapTexture ) return true;
return SUCCEEDED(D3DDev->CreateTexture(side, side, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &EnvmapTexture, 0));
}
#else // (DIRECT3D_VERSION >= 0x900)
static bool __cdecl CreateEnvmapBufferSurface() {
DWORD side = GetEnvmapSide();
if( !side ) return false;
if( EnvmapBufferSurface ) return true;
DDSDESC dsp;
memset(&dsp, 0, sizeof(dsp));
dsp.dwSize = sizeof(dsp);
dsp.dwFlags = DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS;
dsp.dwWidth = side;
dsp.dwHeight = side;
dsp.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY|DDSCAPS_TEXTURE;
if FAILED(DDrawSurfaceCreate(&dsp, &EnvmapBufferSurface))
return -1;
WinVidClearBuffer(EnvmapBufferSurface, NULL, 0);
return 0;
}
#endif // (DIRECT3D_VERSION >= 0x900)
void FreeEnvmapTexture() {
if( EnvmapTexture ) {
EnvmapTexture->Release();
EnvmapTexture = NULL;
}
EnvmapTextureHandle = 0;
}
bool SetEnvmapTexture(LPDDS surface) {
EnvmapTextureHandle = 0;
// Getting centred square area of the screen
int side = MIN(GameVidWidth, GameVidHeight);
int x = (GameVidWidth - side) / 2;
int y = (GameVidHeight - side) / 2;
RECT srcRect = {
.left = GameVidRect.left + x,
.top = GameVidRect.top + y,
.right = GameVidRect.left + x + side,
.bottom = GameVidRect.top + y + side,
};
#if (DIRECT3D_VERSION >= 0x900)
if( EnvmapTexture == NULL && !CreateEnvmapTexture() ) return false;
LPDDS texSurface = NULL;
if SUCCEEDED(EnvmapTexture->GetSurfaceLevel(0, &texSurface)) {
if SUCCEEDED(D3DDev->StretchRect(surface, &srcRect, texSurface, NULL, D3DTEXF_LINEAR)) {
EnvmapTextureHandle = EnvmapTexture;
}
texSurface->Release();
}
#else // (DIRECT3D_VERSION >= 0x900)
if( !CreateEnvmapBufferSurface() ) return false;
if( EnvmapTexture ) FreeEnvmapTexture();
EnvmapBufferSurface->Blt(NULL, surface, &srcRect, DDBLT_WAIT, NULL);
EnvmapTexture = Create3DTexture(EnvmapBufferSurface);
if( EnvmapTexture ) {
EnvmapTexture->GetHandle(D3DDev, &EnvmapTextureHandle);
}
#endif // (DIRECT3D_VERSION >= 0x900)
return ( EnvmapTextureHandle != 0 );
}
HWR_TEXHANDLE GetEnvmapTextureHandle() {
#if (DIRECT3D_VERSION < 0x900)
if( EnvmapTextureHandle ) return EnvmapTextureHandle;
SetEnvmapTexture(CaptureBufferSurface ? CaptureBufferSurface : PrimaryBufferSurface);
#endif // (DIRECT3D_VERSION < 0x900)
return EnvmapTextureHandle;
}
#endif // FEATURE_VIDEOFX_IMPROVED
DWORD GetMaxTextureSize() {
#if (DIRECT3D_VERSION >= 0x900)
return MIN(CurrentDisplayAdapter.caps.MaxTextureWidth, CurrentDisplayAdapter.caps.MaxTextureHeight);
#else // (DIRECT3D_VERSION >= 0x900)
return MIN(CurrentDisplayAdapter.D3DHWDeviceDesc.dwMaxTextureWidth, CurrentDisplayAdapter.D3DHWDeviceDesc.dwMaxTextureHeight);
#endif // (DIRECT3D_VERSION >= 0x900)
}
int GetTextureSideByPage(int page) {
if( page < 0 ) return 256;
page = HWR_TexturePageIndexes[page];
if( page < 0 ) return 256;
return TexturePages[page].width;
}
int GetTextureSideByHandle(HWR_TEXHANDLE handle) {
for( DWORD i=0; i= 0 && HWR_PageHandles[i] == handle ) {
return TexturePages[HWR_TexturePageIndexes[i]].width;
}
}
return 256;
}
void __cdecl CopyBitmapPalette(RGB888 *srcPal, BYTE *srcBitmap, int bitmapSize, RGB888 *destPal) {
int i, j;
#if (DIRECT3D_VERSION < 0x900)
HDC hdc;
PALETTEENTRY firstSysPalEntries[10];
PALETTEENTRY lastSysPalEntries[10];
#endif // (DIRECT3D_VERSION < 0x900)
for( i=0; i<256; ++i ) {
SortBuffer[i]._0 = i;
SortBuffer[i]._1 = 0;
}
for( i=0; i= 0x900)
// middle palette entries
for( j=0; j<256; ++j ) {
destPal[j] = srcPal[SortBuffer[j]._0];
}
#else // (DIRECT3D_VERSION >= 0x900)
hdc = GetDC(NULL);
GetSystemPaletteEntries(hdc, 0, 10, firstSysPalEntries);
GetSystemPaletteEntries(hdc, 246, 10, lastSysPalEntries);
ReleaseDC(NULL, hdc);
// first palette entries
for( i=0; i<8; ++i ) {
destPal[i].red = firstSysPalEntries[i].peRed;
destPal[i].green = firstSysPalEntries[i].peGreen;
destPal[i].blue = firstSysPalEntries[i].peBlue;
}
memset(&destPal[8], 0, 2*sizeof(RGB888));
// middle palette entries
for( i=0, j=10; i<236; ++i, ++j ) {
destPal[j] = srcPal[SortBuffer[i]._0];
}
// last palette entries
memset(&destPal[246], 0, 1*sizeof(RGB888));
for( i=1, j=247; i<10; ++i, ++j ) {
destPal[j].red = lastSysPalEntries[i].peRed;
destPal[j].green = lastSysPalEntries[i].peGreen;
destPal[j].blue = lastSysPalEntries[i].peBlue;
}
#endif // (DIRECT3D_VERSION >= 0x900)
}
BYTE __cdecl FindNearestPaletteEntry(RGB888 *palette, int red, int green, int blue, bool ignoreSysPalette) {
int i;
int diffRed, diffGreen, diffBlue, diffTotal;
int diffMin = INT_MAX;
int palStartIdx = 0;
int palEndIdx = 256;
BYTE result = 0;
#if (DIRECT3D_VERSION < 0x900)
if( ignoreSysPalette ) {
palStartIdx += 10;
palEndIdx -= 10;
}
#endif // (DIRECT3D_VERSION < 0x900)
for( i=palStartIdx; i= 0x900)
int palIndex = GetFreePaletteIndex();
if( palIndex < 0 )
return -1;
TexturePalettes[palIndex] = (RGB888 *)malloc(sizeof(RGB888) * 256);
if( TexturePalettes[palIndex] == NULL )
return -1;
memcpy(TexturePalettes[palIndex], pal, sizeof(RGB888) * 256);
return palIndex;
#else // (DIRECT3D_VERSION >= 0x900)
int palIndex;
PALETTEENTRY palEntries[256];
palIndex = GetFreePaletteIndex();
if( palIndex < 0 )
return -1;
for( int i=0; i<256; ++i ) {
palEntries[i].peRed = pal[i].red;
palEntries[i].peGreen = pal[i].green;
palEntries[i].peBlue = pal[i].blue;
palEntries[i].peFlags = 0;
}
if FAILED(DDraw->CreatePalette(DDPCAPS_ALLOW256|DDPCAPS_8BIT, palEntries, &TexturePalettes[palIndex], NULL))
return -1;
return palIndex;
#endif // (DIRECT3D_VERSION >= 0x900)
}
int __cdecl GetFreePaletteIndex() {
for( DWORD i=0; i= 0x900)
free(TexturePalettes[paletteIndex]);
#else // (DIRECT3D_VERSION >= 0x900)
TexturePalettes[paletteIndex]->Release();
#endif // (DIRECT3D_VERSION >= 0x900)
TexturePalettes[paletteIndex] = NULL;
}
}
void __cdecl SafeFreePalette(int paletteIndex) {
if( paletteIndex >= 0 ) {
FreePalette(paletteIndex);
}
}
#if (DIRECT3D_VERSION >= 0x900)
int __cdecl CreateTexturePage(int width, int height, bool alpha)
#else // (DIRECT3D_VERSION >= 0x900)
int __cdecl CreateTexturePage(int width, int height, LPDIRECTDRAWPALETTE palette)
#endif // (DIRECT3D_VERSION >= 0x900)
{
int pageIndex = GetFreeTexturePageIndex();
if( pageIndex < 0 )
return -1;
memset(&TexturePages[pageIndex], 0, sizeof(TEXPAGE_DESC));
TexturePages[pageIndex].status = 1;
TexturePages[pageIndex].width = width;
TexturePages[pageIndex].height = height;
#if (DIRECT3D_VERSION >= 0x900)
if FAILED(D3DDev->CreateTexture(width, height, 1, 0, alpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &TexturePages[pageIndex].texture, 0))
return -1;
#else // (DIRECT3D_VERSION >= 0x900)
TexturePages[pageIndex].palette = palette;
if( !CreateTexturePageSurface(&TexturePages[pageIndex]) )
return -1;
TexturePageInit(&TexturePages[pageIndex]);
#endif // (DIRECT3D_VERSION >= 0x900)
return pageIndex;
}
#if (DIRECT3D_VERSION < 0x900)
bool __cdecl CreateTexturePageSurface(TEXPAGE_DESC *desc) {
DDSDESC dsp;
memset(&dsp, 0, sizeof(dsp));
dsp.dwSize = sizeof(dsp);
dsp.dwFlags = DDSD_PIXELFORMAT|DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS;
dsp.dwWidth = desc->width;
dsp.dwHeight = desc->height;
dsp.ddpfPixelFormat = TextureFormat.pixelFmt;
dsp.ddsCaps.dwCaps = DDSCAPS_TEXTURE|DDSCAPS_SYSTEMMEMORY;
if FAILED(DDrawSurfaceCreate(&dsp, &desc->sysMemSurface))
return false;
return ( (desc->palette == NULL) || SUCCEEDED(desc->sysMemSurface->SetPalette(desc->palette)) );
}
#endif // (DIRECT3D_VERSION < 0x900)
int __cdecl GetFreeTexturePageIndex() {
for( DWORD i=0; iwidth;;
dsp.dwHeight = page->height;
dsp.ddpfPixelFormat = TextureFormat.pixelFmt;
dsp.ddsCaps.dwCaps = DDSCAPS_ALLOCONLOAD|DDSCAPS_VIDEOMEMORY|DDSCAPS_TEXTURE;
if( FAILED(DDrawSurfaceCreate(&dsp, &page->vidMemSurface)) || page->vidMemSurface == NULL ) {
return false;
}
if( page->palette ) {
colorKey.dwColorSpaceLowValue = 0;
colorKey.dwColorSpaceHighValue = 0;
if( FAILED(page->vidMemSurface->SetPalette(page->palette)) ||
FAILED(page->vidMemSurface->SetColorKey(DDCKEY_SRCBLT, &colorKey)) )
{
page->vidMemSurface->Release();
page->vidMemSurface = NULL;
return false;
}
}
page->texture3d = Create3DTexture(page->vidMemSurface);
if( page->texture3d == NULL ) {
page->vidMemSurface->Release();
page->vidMemSurface = NULL;
return false;
}
if FAILED(page->texture3d->GetHandle(D3DDev, &page->texHandle)) {
page->texture3d->Release();
page->texture3d = NULL;
page->vidMemSurface->Release();
page->vidMemSurface = NULL;
page->texHandle = 0;
return false;
}
return true;
}
LPDIRECT3DTEXTURE2 __cdecl Create3DTexture(LPDDS surface) {
LPDIRECT3DTEXTURE2 texture3d = NULL;
if FAILED(surface->QueryInterface(IID_IDirect3DTexture2, (LPVOID *)&texture3d)) {
return NULL;
}
return texture3d;
}
#endif // (DIRECT3D_VERSION < 0x900)
void __cdecl SafeFreeTexturePage(int pageIndex) {
if( pageIndex >= 0 && (TexturePages[pageIndex].status & 1) != 0 ) {
FreeTexturePage(pageIndex);
}
}
void __cdecl FreeTexturePage(int pageIndex) {
#if (DIRECT3D_VERSION >= 0x900)
if( TexturePages[pageIndex].texture != NULL ) {
TexturePages[pageIndex].texture->Release();
TexturePages[pageIndex].texture = NULL;
}
#else // (DIRECT3D_VERSION >= 0x900)
TexturePageReleaseVidMemSurface(&TexturePages[pageIndex]);
if( TexturePages[pageIndex].sysMemSurface != NULL ) {
TexturePages[pageIndex].sysMemSurface->Release();
TexturePages[pageIndex].sysMemSurface = NULL;
}
#endif // (DIRECT3D_VERSION >= 0x900)
TexturePages[pageIndex].status = 0;
}
#if (DIRECT3D_VERSION < 0x900)
void __cdecl TexturePageReleaseVidMemSurface(TEXPAGE_DESC *page) {
HWR_ResetTexSource();
page->texHandle = 0;
if( page->texture3d ) {
page->texture3d->Release();
page->texture3d = NULL;
}
if( page->vidMemSurface ) {
page->vidMemSurface->Release();
page->vidMemSurface = NULL;
}
}
bool __cdecl ReloadTextures(bool reset) {
bool result = true;
for( DWORD i=0; iLoad(sysMemTexture));
sysMemTexture->Release();
return rc;
}
#endif // (DIRECT3D_VERSION < 0x900)
HWR_TEXHANDLE __cdecl GetTexturePageHandle(int pageIndex) {
if( pageIndex < 0 )
return 0;
#if (DIRECT3D_VERSION >= 0x900)
return TexturePages[pageIndex].texture;
#else // (DIRECT3D_VERSION >= 0x900)
if( TexturePages[pageIndex].vidMemSurface &&
TexturePages[pageIndex].vidMemSurface->IsLost() == DDERR_SURFACELOST )
{
LoadTexturePage(pageIndex, 1);
}
return TexturePages[pageIndex].texHandle;
#endif // (DIRECT3D_VERSION >= 0x900)
}
int __cdecl AddTexturePage8(int width, int height, BYTE *pageBuffer, int palIndex) {
#if (DIRECT3D_VERSION >= 0x900)
int pageIndex = CreateTexturePage(width, height, true);
if( pageIndex < 0 )
return -1;
DDSDESC desc;
if FAILED(TexturePages[pageIndex].texture->LockRect(0, &desc, NULL, 0)) {
return -1;
}
BYTE *src = pageBuffer;
for( int i=0; iUnlockRect(0);
return pageIndex;
#else // (DIRECT3D_VERSION >= 0x900)
int pageIndex;
BYTE *src, *dst;
DDSDESC desc;
if( palIndex < 0 )
return -1;
pageIndex = CreateTexturePage(width, height, TexturePalettes[palIndex]);
if( pageIndex < 0 )
return -1;
if FAILED(WinVidBufferLock(TexturePages[pageIndex].sysMemSurface, &desc, DDLOCK_WRITEONLY|DDLOCK_WAIT))
return -1;
src = pageBuffer;
dst = (BYTE *)desc.lpSurface;
for( int i=0; i= 0x900)
}
int __cdecl AddTexturePage16(int width, int height, BYTE *pageBuffer) {
#if (DIRECT3D_VERSION >= 0x900)
int pageIndex = CreateTexturePage(width, height, true);
if( pageIndex < 0 )
return -1;
DDSDESC desc;
if FAILED(TexturePages[pageIndex].texture->LockRect(0, &desc, NULL, 0)) {
return -1;
}
UINT16 *src = (UINT16 *)pageBuffer;
for( int i=0; i> 7) & 0xF8) | ((*src >> 13) & 0x07);
BYTE g = ((*src >> 2) & 0xF8) | ((*src >> 7) & 0x07);
BYTE b = ((*src << 3) & 0xF8) | ((*src >> 2) & 0x07);
BYTE a = (*src & 0x8000) ? 0xFF : 0;
*dst++ = RGBA_MAKE(r, g, b, a);
++src;
}
}
TexturePages[pageIndex].texture->UnlockRect(0);
return pageIndex;
#else // (DIRECT3D_VERSION >= 0x900)
int i, j, k;
int pageIndex, bytesPerPixel;
BYTE *src, *dst, *subdst;
BYTE srcRed, srcGreen, srcBlue, srcAlpha;
DWORD compatibleColor;
DDSDESC desc;
pageIndex = CreateTexturePage(width, height, NULL);
if( pageIndex < 0 )
return -1;
if FAILED(WinVidBufferLock(TexturePages[pageIndex].sysMemSurface, &desc, DDLOCK_WRITEONLY|DDLOCK_WAIT))
return -1;
if( TexturesHaveCompatibleMasks ) {
src = (BYTE *)pageBuffer;
dst = (BYTE *)desc.lpSurface;
for( i=0; i> 7) & 0xF8;
srcGreen = (*(UINT16 *)src >> 2) & 0xF8;
srcBlue = (*(UINT16 *)src << 3) & 0xF8;
srcAlpha = (*(UINT16 *)src >> 15) & 1;
compatibleColor = CalculateCompatibleColor(&TextureFormat.colorBitMasks, srcRed, srcGreen, srcBlue, srcAlpha);
for( k=0; k>= 8;
}
src += 2;
}
dst += desc.lPitch;
}
}
WinVidBufferUnlock(TexturePages[pageIndex].sysMemSurface, &desc);
LoadTexturePage(pageIndex, false);
return pageIndex;
#endif // (DIRECT3D_VERSION >= 0x900)
}
#if (DIRECT3D_VERSION >= 0x900)
// NOTE: this function is not presented in the original game
int AddTexturePage32(int width, int height, BYTE *pageBuffer, bool alpha) {
int pageIndex = CreateTexturePage(width, height, alpha);
if( pageIndex < 0 )
return -1;
DDSDESC desc;
if FAILED(TexturePages[pageIndex].texture->LockRect(0, &desc, NULL, 0)) {
return -1;
}
DWORD *src = (DWORD *)pageBuffer;
for( int i=0; iUnlockRect(0);
return pageIndex;
}
// NOTE: this function is not presented in the original game
int AddExternalTexture(LPCTSTR fileName, bool alpha) {
int pageIndex = GetFreeTexturePageIndex();
if( pageIndex < 0 )
return -1;
memset(&TexturePages[pageIndex], 0, sizeof(TEXPAGE_DESC));
HRESULT res = D3DXCreateTextureFromFileEx(D3DDev, fileName, D3DX_DEFAULT, D3DX_DEFAULT, 0,
0, alpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8, D3DPOOL_MANAGED,
D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &TexturePages[pageIndex].texture);
if FAILED(res)
return -1;
D3DSURFACE_DESC desc;
if FAILED(TexturePages[pageIndex].texture->GetLevelDesc(0, &desc)) {
TexturePages[pageIndex].texture->Release();
TexturePages[pageIndex].texture = NULL;
return -1;
}
TexturePages[pageIndex].status = 1|2;
TexturePages[pageIndex].width = desc.Width;
TexturePages[pageIndex].height = desc.Height;
return pageIndex;
}
// NOTE: this function is not presented in the original game
bool IsExternalTexture(int page) {
if (page < 0 || page >= (int)ARRAY_SIZE(HWR_TexturePageIndexes))
return false;
page = HWR_TexturePageIndexes[page];
if (page < 0 || page >= (int)ARRAY_SIZE(TexturePages))
return false;
return CHK_ALL(TexturePages[page].status, 1|2);
}
#else // (DIRECT3D_VERSION >= 0x900)
HRESULT CALLBACK EnumTextureFormatsCallback(LPDDSDESC lpDdsd, LPVOID lpContext) {
LPDDPIXELFORMAT lpDDPixFmt = &lpDdsd->ddpfPixelFormat;
if( lpDDPixFmt->dwRGBBitCount < 8 )
return D3DENUMRET_OK;
if( SavedAppSettings.Disable16BitTextures || lpDDPixFmt->dwRGBBitCount < 16 ) {
if( CHK_ANY(lpDDPixFmt->dwFlags, DDPF_PALETTEINDEXED8) ) {
TextureFormat.pixelFmt = *lpDDPixFmt;
TextureFormat.bpp = 8;
TexturesAlphaChannel = false;
if( SavedAppSettings.Disable16BitTextures ) {
TexturesHaveCompatibleMasks = false;
return D3DENUMRET_CANCEL; // NOTE: not presented in the original code
}
}
} else if( CHK_ANY(lpDDPixFmt->dwFlags, DDPF_RGB) ) {
TextureFormat.pixelFmt = *lpDDPixFmt;
TextureFormat.bpp = 16;
TexturesAlphaChannel = CHK_ANY(lpDDPixFmt->dwFlags, DDPF_ALPHAPIXELS);
WinVidGetColorBitMasks(&TextureFormat.colorBitMasks, lpDDPixFmt);
if( TextureFormat.bpp == 16 &&
TextureFormat.colorBitMasks.dwRGBAlphaBitDepth == 1 &&
TextureFormat.colorBitMasks.dwRBitDepth == 5 &&
TextureFormat.colorBitMasks.dwGBitDepth == 5 &&
TextureFormat.colorBitMasks.dwBBitDepth == 5 &&
TextureFormat.colorBitMasks.dwRGBAlphaBitOffset == 15 &&
TextureFormat.colorBitMasks.dwRBitOffset == 10 &&
TextureFormat.colorBitMasks.dwGBitOffset == 5 &&
TextureFormat.colorBitMasks.dwBBitOffset == 0 )
{
TexturesHaveCompatibleMasks = true;
return D3DENUMRET_CANCEL;
}
}
TexturesHaveCompatibleMasks = false;
return D3DENUMRET_OK;
}
HRESULT __cdecl EnumerateTextureFormats() {
memset(&TextureFormat, 0, sizeof(TEXTURE_FORMAT));
HRESULT ret = D3DDev->EnumTextureFormats(EnumTextureFormatsCallback, NULL);
// NOTE: there is no such check in the original code
if( SavedAppSettings.Disable16BitTextures && TextureFormat.bpp < 8 ) {
SavedAppSettings.Disable16BitTextures = false;
ret = D3DDev->EnumTextureFormats(EnumTextureFormatsCallback, NULL);
}
return ret;
}
#endif // (DIRECT3D_VERSION >= 0x900)
void __cdecl CleanupTextures() {
FreeTexturePages();
for( DWORD i=0; i .
*/
#ifndef TEXTURE_H_INCLUDED
#define TEXTURE_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
// NOTE: these functions are not presented in the original game
#ifdef FEATURE_VIDEOFX_IMPROVED
void FreeEnvmapTexture();
bool SetEnvmapTexture(LPDDS surface);
HWR_TEXHANDLE GetEnvmapTextureHandle();
#endif // FEATURE_VIDEOFX_IMPROVED
DWORD GetMaxTextureSize();
int GetTextureSideByPage(int page);
int GetTextureSideByHandle(HWR_TEXHANDLE handle);
void __cdecl CopyBitmapPalette(RGB888 *srcPal, BYTE *srcBitmap, int bitmapSize, RGB888 *destPal); // 0x00455990
BYTE __cdecl FindNearestPaletteEntry(RGB888 *palette, int red, int green, int blue, bool ignoreSysPalette); // 0x00455AD0
void __cdecl SyncSurfacePalettes(void *srcData, int width, int height, int srcPitch, RGB888 *srcPalette, void *dstData, int dstPitch, RGB888 *dstPalette, bool preserveSysPalette); // 0x00455BA0
int __cdecl CreateTexturePalette(RGB888 *pal); // 0x00455C50
int __cdecl GetFreePaletteIndex(); // 0x00455CE0
void __cdecl FreePalette(int paletteIndex); // 0x00455D00
void __cdecl SafeFreePalette(int paletteIndex); // 0x00455D30
#if (DIRECT3D_VERSION >= 0x900)
int __cdecl CreateTexturePage(int width, int height, bool alpha);
#else // (DIRECT3D_VERSION >= 0x900)
int __cdecl CreateTexturePage(int width, int height, LPDIRECTDRAWPALETTE palette); // 0x00455D80
#endif // (DIRECT3D_VERSION >= 0x900)
int __cdecl GetFreeTexturePageIndex(); // 0x00455DF0
#if (DIRECT3D_VERSION < 0x900)
bool __cdecl CreateTexturePageSurface(TEXPAGE_DESC *desc); // 0x00455E10
bool __cdecl TexturePageInit(TEXPAGE_DESC *page); // 0x00455EB0
LPDIRECT3DTEXTURE2 __cdecl Create3DTexture(LPDDS surface); // 0x00456030
#endif // (DIRECT3D_VERSION < 0x900)
void __cdecl SafeFreeTexturePage(int pageIndex); // 0x00456060
void __cdecl FreeTexturePage(int pageIndex); // 0x00456080
#if (DIRECT3D_VERSION < 0x900)
void __cdecl TexturePageReleaseVidMemSurface(TEXPAGE_DESC *page); // 0x004560C0
#endif // (DIRECT3D_VERSION < 0x900)
void __cdecl FreeTexturePages(); // 0x00456100
#if (DIRECT3D_VERSION < 0x900)
bool __cdecl LoadTexturePage(int pageIndex, bool reset); // 0x00456130
bool __cdecl ReloadTextures(bool reset); // 0x004561E0
#endif // (DIRECT3D_VERSION < 0x900)
HWR_TEXHANDLE __cdecl GetTexturePageHandle(int pageIndex); // 0x00456220
int __cdecl AddTexturePage8(int width, int height, BYTE *pageBuffer, int palIndex); // 0x00456260
int __cdecl AddTexturePage16(int width, int height, BYTE *pageBuffer); // 0x00456360
#if (DIRECT3D_VERSION >= 0x900)
int AddTexturePage32(int width, int height, BYTE *pageBuffer, bool alpha); // NOTE: this function is not presented in the original game
int AddExternalTexture(LPCTSTR fileName, bool alpha); // NOTE: this function is not presented in the original game
bool IsExternalTexture(int page); // NOTE: this function is not presented in the original game
#else // (DIRECT3D_VERSION >= 0x900)
HRESULT CALLBACK EnumTextureFormatsCallback(LPDDSDESC lpDdsd, LPVOID lpContext); // 0x00456500
HRESULT __cdecl EnumerateTextureFormats(); // 0x00456620
#endif // (DIRECT3D_VERSION >= 0x900)
void __cdecl CleanupTextures(); // 0x00456650
bool __cdecl InitTextures(); // 0x00456660
#endif // TEXTURE_H_INCLUDED
================================================
FILE: specific/utils.cpp
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/utils.h"
#include "global/vars.h"
static LONGLONG TIME_Ticks, TIME_Start_us;
static double TIME_Frequency, TIME_Period_us;
DWORD __cdecl SyncTicks(DWORD skip) {
double target = (double)skip;
double elapsed = 0.0;
LONGLONG lastTicks = TIME_Ticks;
do {
UpdateTicks();
elapsed = (double)(TIME_Ticks - lastTicks) / TIME_Frequency;
} while( elapsed < target );
return (DWORD)elapsed;
}
// NOTE: redesigned to make it more accurate
void __cdecl UpdateTicks() {
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
TIME_Ticks = counter.QuadPart;
}
bool __cdecl TIME_Init() {
LARGE_INTEGER frequency;
if( !QueryPerformanceFrequency(&frequency) )
return false;
TIME_Frequency = (double)frequency.QuadPart / (double)TICKS_PER_SECOND;
UpdateTicks();
return true;
}
// NOTE: redesigned to make it more accurate
DWORD __cdecl Sync() {
LONGLONG lastTicks = TIME_Ticks;
UpdateTicks();
return (DWORD)((double)(TIME_Ticks - lastTicks) / TIME_Frequency);
}
LPVOID __cdecl UT_LoadResource(LPCTSTR lpName, LPCTSTR lpType) {
HRSRC hRes = FindResource(GameModule, lpName, lpType);
if( hRes == NULL )
return NULL;
HGLOBAL hGlb = LoadResource(GameModule, hRes);
if( hGlb == NULL )
return NULL;
return LockResource(hGlb);;
}
// NOTE: redesigned to make it more accurate
void __cdecl UT_InitAccurateTimer() {
LARGE_INTEGER frequency, counter;
if( QueryPerformanceFrequency(&frequency) ) {
TIME_Period_us = 1.0 / (double)frequency.QuadPart; // Tick period for one microsecond
QueryPerformanceCounter(&counter);
TIME_Start_us = counter.QuadPart;
} else {
TIME_Period_us = 0.0;
TIME_Start_us = 0;
}
}
// NOTE: redesigned to make it more accurate
double __cdecl UT_Microseconds() {
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return (double)(counter.QuadPart - TIME_Start_us) * TIME_Period_us;
}
BOOL __cdecl UT_CenterWindow(HWND hWnd) {
int x, y;
RECT screenArea, windowArea;
SystemParametersInfo(SPI_GETWORKAREA, 0, &screenArea, 0);
GetWindowRect(hWnd, &windowArea);
x = (screenArea.left + screenArea.right) / 2 - (windowArea.right - windowArea.left) / 2;
y = (screenArea.top + screenArea.bottom) / 2 - (windowArea.bottom - windowArea.top) / 2;
return SetWindowPos(hWnd, 0, x, y, -1, -1, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE);
}
LPTSTR __cdecl UT_FindArg(LPCTSTR str) {
LPTSTR next = strstr(CommandLinePtr, str);
return ( next ) ? next += lstrlen(str) : NULL;
}
int __cdecl UT_MessageBox(LPCTSTR lpText, HWND hWnd) {
return MessageBox(hWnd, lpText, MessageBoxName, MB_ICONERROR);
}
int __cdecl UT_ErrorBox(UINT uID, HWND hWnd) {
char str[256];
if( !LoadString(GameModule, uID, str, 256))
return 0;
return UT_MessageBox(str, hWnd);
}
BOOL __cdecl CD_NoteAlert(LPCTSTR lpTemplateName, HWND hWndParent) {
return ( 0 != DialogBoxParam(GameModule, lpTemplateName, hWndParent, DialogBoxProc, 0) );
}
INT_PTR CALLBACK DialogBoxProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch( uMsg ) {
case WM_INITDIALOG :
UT_CenterWindow(hwndDlg);
break;
case WM_COMMAND :
switch( LOWORD(wParam) ) {
case IDOK :
EndDialog(hwndDlg, 1);
return TRUE;
case IDCANCEL :
EndDialog(hwndDlg, 0);
return TRUE;
}
break;
}
return FALSE;
}
void __cdecl UT_MemBlt(BYTE *dstBuf, DWORD dstX, DWORD dstY, DWORD width, DWORD height, DWORD dstPitch,
BYTE *srcBuf, DWORD srcX, DWORD srcY, DWORD srcPitch)
{
BYTE *src = srcBuf + srcX + srcY * srcPitch;
BYTE *dst = dstBuf + dstX + dstY * dstPitch;
for( DWORD i=0; i .
*/
#ifndef UTILS_H_INCLUDED
#define UTILS_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
DWORD __cdecl SyncTicks(DWORD skip); // NOTE: this function is not presented in the original game
void __cdecl UpdateTicks(); // 0x00456680
bool __cdecl TIME_Init(); // 0x004566C0
DWORD __cdecl Sync(); // 0x00456720
LPVOID __cdecl UT_LoadResource(LPCTSTR lpName, LPCTSTR lpType); // 0x00456780
void __cdecl UT_InitAccurateTimer(); // 0x004567C0
double __cdecl UT_Microseconds(); // 0x00456820
BOOL __cdecl UT_CenterWindow(HWND hWnd); // 0x00456870
LPTSTR __cdecl UT_FindArg(LPCTSTR str); // 0x004568E0
int __cdecl UT_MessageBox(LPCTSTR lpText, HWND hWnd); // 0x00456910
int __cdecl UT_ErrorBox(UINT uID, HWND hWnd); // 0x00456930
BOOL __cdecl CD_NoteAlert(LPCTSTR lpTemplateName, HWND hWndParent); // 0x00456980
INT_PTR CALLBACK DialogBoxProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam); // 0x004569B0
void __cdecl UT_MemBlt(BYTE *dstBuf, DWORD dstX, DWORD dstY, DWORD width, DWORD height, DWORD dstPitch,
BYTE *srcBuf, DWORD srcX, DWORD srcY, DWORD srcPitch); // 0x004574A0
#endif // UTILS_H_INCLUDED
================================================
FILE: specific/winmain.cpp
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/winmain.h"
#include "specific/background.h"
#include "specific/fmv.h"
#include "specific/hwr.h"
#include "specific/init.h"
#include "specific/init_3d.h"
#include "specific/init_display.h"
#include "specific/init_input.h"
#include "specific/init_sound.h"
#include "specific/registry.h"
#include "specific/setupdlg.h"
#include "specific/smain.h"
#include "specific/sndpc.h"
#include "specific/texture.h"
#include "specific/utils.h"
#include "specific/winvid.h"
#include "global/resource.h"
#include "global/vars.h"
#if defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED)
#include "modding/gdi_utils.h"
#endif // defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED)
#ifdef FEATURE_NOLEGACY_OPTIONS
extern bool AvoidInterlacedVideoModes;
#endif // FEATURE_NOLEGACY_OPTIONS
#if defined(_MSC_VER)
#include
static void SEH_TR(unsigned int error, EXCEPTION_POINTERS* pExp) {
throw error;
}
#endif // _MSC_VER
#ifdef FEATURE_GOLD
static bool gold = false;
void SetGold(bool state) {
gold = state;
}
bool IsGold() {
return gold;
}
#endif
int __cdecl RenderErrorBox(int errorCode) {
char errorText[128];
LPCTSTR errorMessage = DecodeErrorMessage(errorCode);
wsprintf(errorText, "Render init failed with \"%s\"", errorMessage);
return UT_MessageBox(errorText, 0);
}
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd) {
HWND gameWindow, setupDialog;
int rc, initStatus, appSettingsStatus;
bool isSetupRequested;
GameModule = hInstance;
CommandLinePtr = lpCmdLine;
// If another game instance is already run, show its window
gameWindow = WinVidFindGameWindow();
if( gameWindow != NULL ) {
setupDialog = SE_FindSetupDialog();
SetForegroundWindow(setupDialog ? setupDialog : gameWindow);
exit(0); // NOTE: there may be bugs on some systems if we just return here
}
#ifdef _DEBUG
fflush(stdout);
freopen("./TR2Main.log", "w", stdout);
#endif // _DEBUG
#if defined(_MSC_VER)
_set_se_translator(SEH_TR);
#endif // _MSC_VER
try {
isSetupRequested = ( UT_FindArg("setup") != NULL );
#ifdef FEATURE_GOLD
SetGold(UT_FindArg("gold") != NULL);
#endif
initStatus = Init(isSetupRequested);
if( initStatus == 0 ) {
#if (DIRECT3D_VERSION > 0x500)
char msg[256] = {0};
snprintf(msg, sizeof(msg), "Tomb Raider II requires Microsoft DirectX %d to be installed.", DIRECT3D_VERSION/0x100);
UT_MessageBox(msg, NULL);
#else // (DIRECT3D_VERSION > 0x500)
UT_ErrorBox(IDS_DX5_REQUIRED, NULL); // "Tomb Raider II requires Microsoft DirectX 5 to be installed."
#endif // (DIRECT3D_VERSION > 0x500)
}
if( initStatus != 1 )
goto EXIT;
appSettingsStatus = SE_ReadAppSettings(&SavedAppSettings);
if( appSettingsStatus == 0 )
goto EXIT;
if( isSetupRequested || appSettingsStatus == 2) {
if( !SE_ShowSetupDialog(NULL, appSettingsStatus == 2) )
goto EXIT;
SE_WriteAppSettings(&SavedAppSettings);
if( isSetupRequested )
goto EXIT;
}
while( 0 != (rc = WinGameStart()) ) {
WinGameFinish();
RenderErrorBox(rc);
if( !SE_ShowSetupDialog(NULL, false) )
goto EXIT;
SE_WriteAppSettings(&SavedAppSettings);
}
StopInventory = false;
IsGameToExit = false;
GameMain();
WinGameFinish();
SE_WriteAppSettings(&SavedAppSettings);
} catch(...) {
WinGameFinish();
WinCleanup();
abort();
}
EXIT :
WinCleanup();
exit(AppResultCode); // NOTE: there may be bugs on some systems if we just return here
}
int __cdecl Init(bool skipCDInit) {
if( !skipCDInit && !CD_Init() )
return 2;
UT_InitAccurateTimer();
#ifdef FEATURE_NOLEGACY_OPTIONS
if( OpenGameRegistryKey(REG_SYSTEM_KEY) ) {
GetRegistryBoolValue(REG_AVOID_INTERLACED, &AvoidInterlacedVideoModes, false);
CloseGameRegistryKey();
}
#endif // FEATURE_NOLEGACY_OPTIONS
if(
#if defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED)
GDI_Init() &&
#endif // defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED)
WinVidInit() &&
Direct3DInit() &&
RenderInit() &&
InitTextures() &&
WinSndInit() &&
WinInputInit() &&
TIME_Init() &&
HWR_Init() &&
BGND_Init() )
{
FMV_Init(); // FMV Init is not critical to fail whole game
return 1;
}
return 0;
}
void __cdecl WinCleanup() {
WinVidFreeWindow();
CD_Cleanup();
FMV_Cleanup();
#if defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED)
GDI_Cleanup();
#endif // defined(FEATURE_SCREENSHOT_IMPROVED) || defined(FEATURE_BACKGROUND_IMPROVED)
}
int __cdecl WinGameStart() {
try {
WinVidStart();
RenderStart(true);
WinSndStart(NULL);
WinInStart();
} catch(int error) {
return error;
}
return 0;
}
void __cdecl WinGameFinish() {
WinInFinish();
WinSndFinish();
RenderFinish(true);
WinVidFinish();
WinVidHideGameWindow();
if( *StringToShow )
MessageBox(NULL, StringToShow, NULL, MB_ICONWARNING);
}
void __cdecl S_ExitSystem(LPCTSTR message) {
ShutdownGame();
lstrcpy(StringToShow, message);
WinGameFinish();
WinCleanup();
exit(1); // the app is terminated here
}
/*
* Inject function
*/
void Inject_WinMain() {
INJECT(0x0044E5A0, RenderErrorBox);
INJECT(0x0044E5E0, WinMain);
INJECT(0x0044E7B0, Init);
INJECT(0x0044E830, WinCleanup);
INJECT(0x0044E860, WinGameStart);
INJECT(0x0044E8E0, WinGameFinish);
INJECT(0x0044E950, S_ExitSystem);
}
================================================
FILE: specific/winmain.h
================================================
/*
* Copyright (c) 2017-2018 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef WINMAIN_H_INCLUDED
#define WINMAIN_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
int __cdecl RenderErrorBox(int errorCode); // 0x0044E5A0
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd); // 0x0044E5E0
int __cdecl Init(bool skipCDInit); // 0x0044E7B0
void __cdecl WinCleanup(); // 0x0044E830
int __cdecl WinGameStart(); // 0x0044E860
void __cdecl WinGameFinish(); // 0x0044E8E0
void __cdecl S_ExitSystem(LPCTSTR message); // 0x0044E950
#endif // WINMAIN_H_INCLUDED
================================================
FILE: specific/winvid.cpp
================================================
/*
* Copyright (c) 2017-2021 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#include "global/precompiled.h"
#include "specific/winvid.h"
#include "specific/init_3d.h"
#include "specific/init_display.h"
#include "specific/sndpc.h"
#include "global/resource.h"
#include "global/vars.h"
#include "global/memmem.h"
#if (DIRECT3D_VERSION > 0x500)
DISPLAY_ADAPTER CurrentDisplayAdapter;
#endif // (DIRECT3D_VERSION > 0x500)
#ifdef FEATURE_WINDOW_STYLE_FIX
static void setWindowStyle(bool isFullScreen) {
static const DWORD fullScreenFlags = WS_POPUP;
static const DWORD windowedFlags = WS_OVERLAPPEDWINDOW; // WS_BORDER|WS_DLGFRAME|WS_SYSMENU|WS_SIZEBOX|WS_MINIMIZEBOX|WS_MAXIMIZEBOX
DWORD style = GetWindowLong(HGameWindow, GWL_STYLE);
style &= ~(isFullScreen ? windowedFlags : fullScreenFlags);
style |= (isFullScreen ? fullScreenFlags : windowedFlags);
SetWindowLong(HGameWindow, GWL_STYLE, style);
}
#endif // FEATURE_WINDOW_STYLE_FIX
#ifdef FEATURE_NOLEGACY_OPTIONS
bool AvoidInterlacedVideoModes = false;
#endif // FEATURE_NOLEGACY_OPTIONS
#ifdef FEATURE_INPUT_IMPROVED
#include "modding/raw_input.h"
#include "modding/joy_output.h"
#endif // FEATURE_INPUT_IMPROVED
static bool InsertDisplayModeInListSorted(DISPLAY_MODE_LIST *modeList, DISPLAY_MODE *srcMode) {
DISPLAY_MODE_NODE *node = NULL;
DISPLAY_MODE *dstMode = NULL;
if( !modeList->head || CompareVideoModes(srcMode, &modeList->head->body) ) {
dstMode = InsertDisplayModeInListHead(modeList);
goto FILL;
}
for( node = modeList->head; node; node = node->next ) {
if( CompareVideoModes(srcMode, &node->body) ) {
dstMode = InsertDisplayMode(modeList, node);
goto FILL;
#if (DIRECT3D_VERSION >= 0x900)
} else if( !memcmp(srcMode, &node->body, sizeof(DISPLAY_MODE)) ) {
return false;
#endif // (DIRECT3D_VERSION >= 0x900)
}
}
dstMode = InsertDisplayModeInListTail(modeList);
FILL:
if( dstMode ) {
*dstMode = *srcMode;
return true;
}
return false;
}
static bool DisplayModeListCopy(DISPLAY_MODE_LIST *dst, DISPLAY_MODE_LIST *src) {
if( dst == NULL || src == NULL || dst == src )
return false;
DISPLAY_MODE_NODE *node;
DISPLAY_MODE *dstMode;
DisplayModeListDelete(dst);
for( node = src->head; node; node = node->next ) {
dstMode = InsertDisplayModeInListTail(dst);
*dstMode = node->body;
}
return true;
}
bool FlaggedStringCopy(STRING_FLAGGED *dst, STRING_FLAGGED *src) {
if( dst == NULL || src == NULL || dst == src || !src->isPresented )
return false;
size_t srcLen = lstrlen(src->lpString);
dst->isPresented = false;
dst->lpString = new char[srcLen + 1];
if( dst->lpString == NULL )
return false;
if( srcLen > 0 ) {
lstrcpy(dst->lpString, src->lpString);
} else {
*dst->lpString = 0;
}
dst->isPresented = true;
return true;
}
#if (DIRECT3D_VERSION < 0x900)
bool __cdecl DDrawCreate(LPGUID lpGUID) {
if FAILED(DirectDrawCreate(lpGUID, &DDrawInterface, 0))
return false;
if FAILED(DDrawInterface->QueryInterface(IID_IDirectDraw2, (LPVOID *)&DDraw))
return false;
DDraw->SetCooperativeLevel(HGameWindow, DDSCL_NORMAL);
return true;
}
void __cdecl DDrawRelease() {
if( DDraw ) {
DDraw->Release();
DDraw = NULL;
}
if( DDrawInterface ) {
DDrawInterface->Release();
DDrawInterface = NULL;
}
}
#endif // (DIRECT3D_VERSION < 0x900)
void __cdecl GameWindowCalculateSizeFromClient(int *width, int *height) {
DWORD style, styleEx;
RECT rect = {0, 0, *width, *height};
style = GetWindowLong(HGameWindow, GWL_STYLE);
styleEx = GetWindowLong(HGameWindow, GWL_EXSTYLE);
AdjustWindowRectEx(&rect, style, FALSE, styleEx);
*width = rect.right - rect.left;
*height = rect.bottom - rect.top;
}
void __cdecl GameWindowCalculateSizeFromClientByZero(int *width, int *height) {
DWORD style, styleEx;
RECT rect = {0, 0, 0, 0};
style = GetWindowLong(HGameWindow, GWL_STYLE);
styleEx = GetWindowLong(HGameWindow, GWL_EXSTYLE);
AdjustWindowRectEx(&rect, style, FALSE, styleEx);
*width += rect.left - rect.right;
*height += rect.top - rect.bottom;;
}
void __cdecl WinVidSetMinWindowSize(int width, int height) {
MinWindowClientWidth = width;
MinWindowClientHeight = height;
GameWindowCalculateSizeFromClient(&width, &height);
MinWindowWidth = width;
MinWindowHeight = height;
IsMinWindowSizeSet = true;
}
void __cdecl WinVidClearMinWindowSize() {
IsMinWindowSizeSet = false;
}
void __cdecl WinVidSetMaxWindowSize(int width, int height) {
MaxWindowClientWidth = width;
MaxWindowClientHeight = height;
GameWindowCalculateSizeFromClient(&width, &height);
MaxWindowWidth = width;
MaxWindowHeight = height;
IsMaxWindowSizeSet = true;
}
void __cdecl WinVidClearMaxWindowSize() {
IsMaxWindowSizeSet = false;
}
int __cdecl CalculateWindowWidth(int width, int height) {
switch( SavedAppSettings.AspectMode ) {
case AM_4_3 :
return height*4/3;
case AM_16_9 :
return height*16/9;
default :
break;
}
return width;
}
int __cdecl CalculateWindowHeight(int width, int height) {
switch( SavedAppSettings.AspectMode ) {
case AM_4_3 :
return width*3/4;
case AM_16_9 :
return width*9/16;
default :
break;
}
return height;
}
bool __cdecl WinVidGetMinMaxInfo(LPMINMAXINFO info) {
if( !IsGameWindowCreated )
return false;
if( IsGameFullScreen ) {
info->ptMinTrackSize.x = FullScreenWidth;
info->ptMinTrackSize.y = FullScreenHeight;
info->ptMaxTrackSize.x = FullScreenWidth;
info->ptMaxTrackSize.y = FullScreenHeight;
info->ptMaxSize.x = FullScreenWidth;
info->ptMaxSize.y = FullScreenHeight;
return true;
}
if( IsMinWindowSizeSet ) {
info->ptMinTrackSize.x = MinWindowWidth;
info->ptMinTrackSize.y = MinWindowHeight;
}
if( IsMinMaxInfoSpecial ) {
int newWindowWidth = GameWindowWidth;
int newWindowHeight = GameWindowHeight;
GameWindowCalculateSizeFromClient(&newWindowWidth, &newWindowHeight);
info->ptMaxTrackSize.x = newWindowWidth;
info->ptMaxTrackSize.y = newWindowHeight;
info->ptMaxSize.x = newWindowWidth;
info->ptMaxSize.y = newWindowHeight;
}
else if( IsMaxWindowSizeSet ) {
info->ptMaxTrackSize.x = MaxWindowWidth;
info->ptMaxTrackSize.y = MaxWindowHeight;
info->ptMaxSize.x = MaxWindowWidth;
info->ptMaxSize.y = MaxWindowHeight;
}
return ( IsMinWindowSizeSet || IsMaxWindowSizeSet );
}
HWND __cdecl WinVidFindGameWindow() {
return FindWindow(GameClassName, GameWindowName);
}
bool __cdecl WinVidSpinMessageLoop(bool needWait) {
static int messageLoopCounter = 0;
MSG msg;
if( IsMessageLoopClosed )
return false;
++messageLoopCounter;
do {
if( needWait )
WaitMessage();
else
needWait = true;
while( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) {
TranslateMessage(&msg);
DispatchMessage(&msg);
if( msg.message == WM_QUIT ) {
AppResultCode = msg.wParam;
IsGameToExit = true;
StopInventory = true;
IsMessageLoopClosed = true;
--messageLoopCounter;
return false;
}
}
} while( !IsGameWindowActive || IsGameWindowMinimized );
--messageLoopCounter;
return true;
}
void __cdecl WinVidShowGameWindow(int nCmdShow) {
if( nCmdShow != SW_SHOW || !IsGameWindowShow ) {
IsGameWindowUpdating = TRUE;
ShowWindow(HGameWindow, nCmdShow);
UpdateWindow(HGameWindow);
IsGameWindowUpdating = FALSE;
IsGameWindowShow = TRUE;
}
}
void __cdecl WinVidHideGameWindow() {
if( IsGameWindowShow ) {
IsGameWindowUpdating = TRUE;
ShowWindow(HGameWindow, SW_HIDE);
UpdateWindow(HGameWindow);
IsGameWindowUpdating = FALSE;
IsGameWindowShow = FALSE;
}
}
void __cdecl WinVidSetGameWindowSize(int width, int height) {
GameWindowCalculateSizeFromClient(&width, &height);
SetWindowPos(HGameWindow, NULL, 0, 0, width, height, SWP_NOCOPYBITS|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE);
}
#if (DIRECT3D_VERSION < 0x900)
bool __cdecl ShowDDrawGameWindow(bool active) {
DWORD flags;
HRESULT rc;
RECT rect;
if( !HGameWindow || !DDraw )
return false;
if( IsDDrawGameWindowShow )
return true;
GetWindowRect(HGameWindow, &rect);
GameWindow_X = rect.left;
GameWindow_Y = rect.top;
if( active )
WinVidShowGameWindow(SW_SHOW);
flags = DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT | DDSCL_FULLSCREEN;
if( !active )
flags |= DDSCL_NOWINDOWCHANGES;
IsGameWindowUpdating = TRUE;
rc = DDraw->SetCooperativeLevel(HGameWindow, flags);
IsGameWindowUpdating = FALSE;
if SUCCEEDED(rc) {
IsDDrawGameWindowShow = TRUE;
return true;
}
return false;
}
bool __cdecl HideDDrawGameWindow() {
bool result = false;
if( HGameWindow == NULL || DDraw == NULL )
return false;
if( !IsDDrawGameWindowShow )
return true;
WinVidHideGameWindow();
IsGameWindowUpdating = TRUE;
if SUCCEEDED(DDraw->SetCooperativeLevel(HGameWindow, DDSCL_NORMAL)) {
IsDDrawGameWindowShow = FALSE;
SetWindowPos(HGameWindow, 0, GameWindow_X, GameWindow_Y, 0, 0, SWP_NOCOPYBITS|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE);
result = true;
}
IsGameWindowUpdating = FALSE;
return result;
}
HRESULT __cdecl DDrawSurfaceCreate(LPDDSDESC dsp, LPDDS *surface) {
LPDIRECTDRAWSURFACE subSurface;
HRESULT rc = DDraw->CreateSurface(dsp, &subSurface, NULL);
if SUCCEEDED(rc) {
rc = subSurface->QueryInterface(IID_IDirectDrawSurface3, (LPVOID *)surface);
subSurface->Release();
}
return rc;
}
HRESULT __cdecl DDrawSurfaceRestoreLost(LPDDS surface1, LPDDS surface2, bool blank) {
if( surface1 == NULL ) // NOTE: additional check just in case
return 0;
HRESULT rc = surface1->IsLost();
if( rc != DDERR_SURFACELOST )
return rc;
rc = (surface2 ? surface2 : surface1)->Restore();
if( blank && SUCCEEDED(rc) )
WinVidClearBuffer(surface1, 0, 0);
return rc;
}
bool __cdecl WinVidClearBuffer(LPDDS surface, LPRECT rect, DWORD fillColor) {
DDBLTFX bltFx;
if( surface == NULL ) // NOTE: additional check just in case
return 0;
memset(&bltFx, 0, sizeof(DDBLTFX));
bltFx.dwSize = sizeof(DDBLTFX);
bltFx.dwFillColor = fillColor;
return SUCCEEDED(surface->Blt(rect, NULL, NULL, DDBLT_WAIT|DDBLT_COLORFILL, &bltFx));
}
HRESULT __cdecl WinVidBufferLock(LPDDS surface, LPDDSDESC desc, DWORD flags) {
memset(desc, 0, sizeof(DDSDESC));
desc->dwSize = sizeof(DDSDESC);
HRESULT result = surface->Lock(NULL, desc, flags, NULL);
if SUCCEEDED(result) {
++LockedBufferCount;
}
return result;
}
HRESULT __cdecl WinVidBufferUnlock(LPDDS surface, LPDDSDESC desc) {
HRESULT result = surface->Unlock(desc->lpSurface);
if SUCCEEDED(result) {
--LockedBufferCount;
}
return result;
}
bool __cdecl WinVidCopyBitmapToBuffer(LPDDS surface, BYTE *bitmap) {
DWORD i;
BYTE *src, *dst;
DDSDESC desc;
if( surface == NULL || bitmap == NULL ) // NOTE: additional check just in case
return false;
if FAILED(WinVidBufferLock(surface, &desc, DDLOCK_WRITEONLY|DDLOCK_WAIT))
return false;
src = bitmap;
dst = (BYTE *)desc.lpSurface;
for( i=0; idwRBitMask = pixelFormat->dwRBitMask;
bm->dwGBitMask = pixelFormat->dwGBitMask;
bm->dwBBitMask = pixelFormat->dwBBitMask;
bm->dwRGBAlphaBitMask = pixelFormat->dwRGBAlphaBitMask;
BitMaskGetNumberOfBits(bm->dwRBitMask, &bm->dwRBitDepth, &bm->dwRBitOffset);
BitMaskGetNumberOfBits(bm->dwGBitMask, &bm->dwGBitDepth, &bm->dwGBitOffset);
BitMaskGetNumberOfBits(bm->dwBBitMask, &bm->dwBBitDepth, &bm->dwBBitOffset);
BitMaskGetNumberOfBits(bm->dwRGBAlphaBitMask, &bm->dwRGBAlphaBitDepth, &bm->dwRGBAlphaBitOffset);
}
void __cdecl BitMaskGetNumberOfBits(DWORD bitMask, DWORD *bitDepth, DWORD *bitOffset) {
DWORD i;
if( !bitMask ) {
*bitOffset = 0;
*bitDepth = 0;
return;
}
for( i = 0; (bitMask & 1) == 0; ++i ) {
bitMask >>= 1;
}
*bitOffset = i;
for( i = 0; bitMask != 0; ++i ) {
bitMask >>= 1;
}
*bitDepth = i;
}
DWORD __cdecl CalculateCompatibleColor(COLOR_BIT_MASKS *mask, int red, int green, int blue, int alpha) {
return (red >> (8 - mask->dwRBitDepth) << mask->dwRBitOffset) |
(green >> (8 - mask->dwGBitDepth) << mask->dwGBitOffset) |
(blue >> (8 - mask->dwBBitDepth) << mask->dwBBitOffset) |
(alpha >> (8 - mask->dwRGBAlphaBitDepth) << mask->dwRGBAlphaBitOffset);
}
#endif // (DIRECT3D_VERSION < 0x900)
bool __cdecl WinVidGetDisplayMode(DISPLAY_MODE *dispMode) {
#if (DIRECT3D_VERSION >= 0x900)
bool d3dClean = false;
if( D3D == NULL ) {
if( !D3DCreate() ) return false;
d3dClean = true;
}
D3DDISPLAYMODE mode;
HRESULT res = D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &mode);
if( d3dClean ) D3DRelease();
if( SUCCEEDED(res) && mode.Format == D3DFMT_X8R8G8B8 ) {
dispMode->width = mode.Width;
dispMode->height = mode.Height;
dispMode->bpp = 32;
dispMode->vga = VGA_NoVga;
return true;
}
return false;
#else // (DIRECT3D_VERSION >= 0x900)
DDSDESC dsp;
memset(&dsp, 0, sizeof(dsp));
dsp.dwSize = sizeof(dsp);
if( SUCCEEDED(DDraw->GetDisplayMode(&dsp)) &&
((dsp.dwFlags & DDSD_WIDTH) != 0) &&
((dsp.dwFlags & DDSD_HEIGHT) != 0) &&
((dsp.dwFlags & DDSD_PIXELFORMAT) != 0) &&
((dsp.ddpfPixelFormat.dwFlags & DDPF_RGB) != 0) )
{
dispMode->width = dsp.dwWidth;
dispMode->height = dsp.dwHeight;
dispMode->bpp = dsp.ddpfPixelFormat.dwRGBBitCount;
if( (dsp.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) != 0 )
dispMode->vga = VGA_256Color;
else
dispMode->vga = VGA_NoVga;
return true;
}
return false;
#endif // (DIRECT3D_VERSION >= 0x900)
}
bool __cdecl WinVidGoFullScreen(DISPLAY_MODE *dispMode) {
FullScreenWidth = dispMode->width;
FullScreenHeight = dispMode->height;
FullScreenBPP = dispMode->bpp;
FullScreenVGA = dispMode->vga;
#ifdef FEATURE_WINDOW_STYLE_FIX
setWindowStyle(true);
#endif // FEATURE_WINDOW_STYLE_FIX
#if (DIRECT3D_VERSION < 0x900)
HRESULT rc;
if( !ShowDDrawGameWindow(true) )
goto FAIL;
IsGameWindowUpdating = true;
rc = DDraw->SetDisplayMode(dispMode->width, dispMode->height, dispMode->bpp, 0, (dispMode->vga == VGA_Standard) ? DDSDM_STANDARDVGAMODE : 0);
IsGameWindowUpdating = false;
if FAILED(rc)
goto FAIL;
#endif // (DIRECT3D_VERSION < 0x900)
IsGameFullScreen = true;
return true;
#if (DIRECT3D_VERSION < 0x900)
FAIL :
#ifdef FEATURE_WINDOW_STYLE_FIX
setWindowStyle(false);
#endif // FEATURE_WINDOW_STYLE_FIX
return false;
#endif // (DIRECT3D_VERSION < 0x900)
}
bool __cdecl WinVidGoWindowed(int width, int height, DISPLAY_MODE *dispMode) {
int maxWidth, maxHeight;
RECT rect;
#if (DIRECT3D_VERSION >= 0x900)
if( !WinVidGetDisplayMode(dispMode) )
return false;
#else // (DIRECT3D_VERSION >= 0x900)
if( !HideDDrawGameWindow() || !WinVidGetDisplayMode(dispMode) )
return false;
#endif // (DIRECT3D_VERSION >= 0x900)
#ifdef FEATURE_WINDOW_STYLE_FIX
setWindowStyle(false);
#endif // FEATURE_WINDOW_STYLE_FIX
maxWidth = dispMode->width;
maxHeight = CalculateWindowHeight(dispMode->width, dispMode->height);
if( maxHeight > dispMode->height ) {
maxHeight = dispMode->height;
maxWidth = CalculateWindowWidth(dispMode->width, dispMode->height);
}
WinVidSetMaxWindowSize(maxWidth, maxHeight);
if( width > maxWidth || height > maxHeight ) {
width = maxWidth;
height = maxHeight;
}
IsGameFullScreen = false;
IsGameWindowUpdating = true;
WinVidSetGameWindowSize(width, height);
IsGameWindowUpdating = false;
GetClientRect(HGameWindow, &rect);
MapWindowPoints(HGameWindow, NULL, (LPPOINT)&rect, 2);
if( (rect.left > 0 || rect.right < dispMode->width) &&
(rect.top > 0 || rect.bottom < dispMode->height) )
{
WinVidShowGameWindow(SW_SHOW);
} else {
WinVidShowGameWindow(SW_MAXIMIZE);
}
dispMode->width = width;
dispMode->height = height;
return true;
}
void __cdecl WinVidSetDisplayAdapter(DISPLAY_ADAPTER *dispAdapter) {
bool rc;
DISPLAY_MODE dispMode;
dispAdapter->screenWidth = 0;
#if (DIRECT3D_VERSION < 0x900)
dispAdapter->swWindowedSupported = false;
dispAdapter->hwWindowedSupported = false;
// Primary adapter GUID is NULL. Secondary adapter is not applicable
if( dispAdapter->lpAdapterGuid != NULL )
return;
if( !DDrawCreate(NULL) ) return;
#endif // (DIRECT3D_VERSION < 0x900)
rc = WinVidGetDisplayMode(&dispMode);
#if (DIRECT3D_VERSION < 0x900)
DDrawRelease();
#endif // (DIRECT3D_VERSION < 0x900)
if( !rc ) return;
dispMode.width &= ~0x1F;
if( dispMode.width*3/4 > dispMode.height )
dispMode.width = (dispMode.height*4/3) & ~0x1F;
dispAdapter->screenWidth = dispMode.width;
#if (DIRECT3D_VERSION < 0x900)
dispAdapter->swWindowedSupported = ( dispMode.vga == VGA_256Color );
dispAdapter->hwWindowedSupported = ( dispAdapter->hwRenderSupported &&
((dispAdapter->D3DHWDeviceDesc.dwFlags & D3DDD_DEVICERENDERBITDEPTH) != 0) &&
((GetRenderBitDepth(dispMode.bpp) & dispAdapter->D3DHWDeviceDesc.dwDeviceRenderBitDepth) != 0) );
#endif // (DIRECT3D_VERSION < 0x900)
}
bool __thiscall CompareVideoModes(DISPLAY_MODE *mode1, DISPLAY_MODE *mode2) {
#ifdef FEATURE_NOLEGACY_OPTIONS
if( mode1->bpp < mode2->bpp ) return true;
if( mode1->bpp > mode2->bpp ) return false;
if( mode1->width < mode2->width ) return true;
if( mode1->width > mode2->width ) return false;
if( mode1->height < mode2->height ) return true;
if( mode1->height > mode2->height ) return false;
#else // !FEATURE_NOLEGACY_OPTIONS
DWORD square1 = mode1->width * mode1->height;
DWORD square2 = mode2->width * mode2->height;
if( square1 < square2 ) return true;
if( square1 > square2 ) return false;
if( mode1->bpp < mode2->bpp ) return true;
if( mode1->bpp > mode2->bpp ) return false;
#endif // FEATURE_NOLEGACY_OPTIONS
if( mode1->vga < mode2->vga ) return true;
if( mode1->vga > mode2->vga ) return false;
// equal state
return false;
}
#ifdef FEATURE_NOLEGACY_OPTIONS
static void DeleteDisplayMode(DISPLAY_MODE_LIST *modeList, DISPLAY_MODE_NODE *node) {
if( !modeList || !node ) return;
DISPLAY_MODE_NODE *previous = node->previous;
DISPLAY_MODE_NODE *next = node->next;
if( previous ) previous->next = next;
if( next ) next->previous = previous;
if( modeList->head == node ) modeList->head = next;
if( modeList->tail == node ) modeList->tail = previous;
if( modeList->dwCount ) --modeList->dwCount;
delete(node);
}
static DWORD GetProgressiveDisplayModes(DWORD bpp, DEVMODE *modes, DWORD modeNum) {
DWORD idx = 0;
DWORD num = 0;
if( modes == NULL ) {
DEVMODE mode;
memset(&mode, 0, sizeof(mode));
mode.dmSize = sizeof(mode);
while( EnumDisplaySettings(NULL, idx++, &mode) ) {
if( mode.dmBitsPerPel == bpp && !CHK_ANY(mode.dmDisplayFlags, DM_INTERLACED) ) {
++num;
}
}
} else {
memset(modes, 0, sizeof(DEVMODE) * modeNum);
while( num < modeNum ) {
modes[num].dmSize = sizeof(DEVMODE);
if( !EnumDisplaySettings(NULL, idx++, &modes[num]) ) {
break;
}
if( modes[num].dmBitsPerPel == bpp && !CHK_ANY(modes[num].dmDisplayFlags, DM_INTERLACED) ) {
++num;
}
}
}
return num;
}
static bool IsModeInList(DISPLAY_MODE *mode, DEVMODE *modes, DWORD modeNum) {
if( !mode || !modes || !modeNum ) return false;
for( DWORD i = 0; i < modeNum; ++i ) {
if( modes[i].dmPelsWidth == (DWORD)mode->width &&
modes[i].dmPelsHeight == (DWORD)mode->height &&
modes[i].dmBitsPerPel == (DWORD)mode->bpp )
{
return true;
}
}
return false;
}
static void FilterDisplayModes(DISPLAY_MODE_LIST *modeList) {
DWORD wlistSize = 0;
DEVMODE *whitelist = NULL;
DISPLAY_MODE_NODE *mode, *next;
int bppMax = 8;
for( mode = modeList->head; mode; mode = mode->next ) {
CLAMPL(bppMax, mode->body.bpp);
}
if( AvoidInterlacedVideoModes ) {
wlistSize = GetProgressiveDisplayModes(bppMax, NULL, 0);
if( wlistSize ) {
whitelist = (DEVMODE *)malloc(sizeof(DEVMODE) * wlistSize);
if( whitelist ) {
GetProgressiveDisplayModes(bppMax, whitelist, wlistSize);
}
}
}
for( mode = modeList->head; mode; mode = next ) {
next = mode->next;
if( mode->body.bpp < bppMax || (whitelist && !IsModeInList(&mode->body, whitelist, wlistSize)) ) {
DeleteDisplayMode(modeList, mode);
}
}
if( whitelist ) {
free(whitelist);
}
}
#endif // FEATURE_NOLEGACY_OPTIONS
bool __cdecl WinVidGetDisplayModes() {
DISPLAY_ADAPTER_NODE *adapter;
#if (DIRECT3D_VERSION >= 0x900)
bool d3dClean = false;
if( D3D == NULL ) {
if( !D3DCreate() ) return false;
d3dClean = true;
}
for( adapter = DisplayAdapterList.head; adapter; adapter = adapter->next ) {
UINT num = D3D->GetAdapterModeCount(adapter->body.index, D3DFMT_X8R8G8B8);
for( UINT i = 0; i < num; ++i ) {
D3DDISPLAYMODE mode;
if FAILED(D3D->EnumAdapterModes(adapter->body.index, D3DFMT_X8R8G8B8, i, &mode)) continue;
DISPLAY_MODE videoMode = {(int)mode.Width, (int)mode.Height, 32, VGA_NoVga};
InsertDisplayModeInListSorted(&adapter->body.hwDispModeList, &videoMode);
InsertDisplayModeInListSorted(&adapter->body.swDispModeList, &videoMode);
}
FilterDisplayModes(&adapter->body.hwDispModeList);
}
if( d3dClean ) D3DRelease();
#else // (DIRECT3D_VERSION >= 0x900)
for( adapter = DisplayAdapterList.head; adapter; adapter = adapter->next ) {
DDrawCreate(adapter->body.lpAdapterGuid);
ShowDDrawGameWindow(false);
#ifdef FEATURE_NOLEGACY_OPTIONS
DDraw->EnumDisplayModes(0, NULL, (LPVOID)&adapter->body, EnumDisplayModesCallback);
FilterDisplayModes(&adapter->body.hwDispModeList);
#else // FEATURE_NOLEGACY_OPTIONS
DDraw->EnumDisplayModes(DDEDM_STANDARDVGAMODES, NULL, (LPVOID)&adapter->body, EnumDisplayModesCallback);
#endif // FEATURE_NOLEGACY_OPTIONS
HideDDrawGameWindow();
DDrawRelease();
}
#endif // (DIRECT3D_VERSION >= 0x900)
return true;
}
#if (DIRECT3D_VERSION < 0x900)
HRESULT WINAPI EnumDisplayModesCallback(LPDDSDESC lpDDSurfaceDesc, LPVOID lpContext) {
DISPLAY_ADAPTER *adapter = (DISPLAY_ADAPTER *)lpContext;
DISPLAY_MODE videoMode;
VGA_MODE vgaMode = VGA_NoVga;
DWORD renderBitDepth = 0;
bool swRendererSupported = false;
if( (lpDDSurfaceDesc->dwFlags & (DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT)) != (DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT) ||
(lpDDSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_RGB) == 0 )
{
return DDENUMRET_OK;
}
if( (lpDDSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) != 0 &&
lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount == 8 )
{
#ifdef FEATURE_NOLEGACY_OPTIONS
// Check software renderer requirements for 8 bit display modes
if( lpDDSurfaceDesc->dwHeight % 4 != 0 ||
CHK_ANY(lpDDSurfaceDesc->ddsCaps.dwCaps, DDSCAPS_MODEX|DDSCAPS_STANDARDVGAMODE) )
{
return DDENUMRET_OK;
}
vgaMode = VGA_256Color;
#else // FEATURE_NOLEGACY_OPTIONS
if( CHK_ANY(lpDDSurfaceDesc->ddsCaps.dwCaps, DDSCAPS_MODEX) ) {
vgaMode = VGA_ModeX;
} else if( CHK_ANY(lpDDSurfaceDesc->ddsCaps.dwCaps, DDSCAPS_STANDARDVGAMODE) ) {
vgaMode = VGA_Standard;
} else {
vgaMode = VGA_256Color;
}
#endif // FEATURE_NOLEGACY_OPTIONS
if( lpDDSurfaceDesc->dwWidth == 320 &&
lpDDSurfaceDesc->dwHeight == 200 &&
(!adapter->isVgaMode1Presented || vgaMode < adapter->vgaMode1.vga) )
{
adapter->vgaMode1.width = 320;
adapter->vgaMode1.height = 200;
adapter->vgaMode1.bpp = 8;
adapter->vgaMode1.vga = vgaMode;
adapter->isVgaMode1Presented = true;
}
if( lpDDSurfaceDesc->dwWidth == 640 &&
lpDDSurfaceDesc->dwHeight == 480 &&
(!adapter->isVgaMode2Presented || vgaMode < adapter->vgaMode2.vga) )
{
adapter->vgaMode2.width = 640;
adapter->vgaMode2.height = 480;
adapter->vgaMode2.bpp = 8;
adapter->vgaMode2.vga = vgaMode;
adapter->isVgaMode2Presented = true;
}
swRendererSupported = true;
}
videoMode.width = lpDDSurfaceDesc->dwWidth;
videoMode.height = lpDDSurfaceDesc->dwHeight;
videoMode.bpp = lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount;
videoMode.vga = vgaMode;
renderBitDepth = GetRenderBitDepth(lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount);
if( adapter->hwRenderSupported && 0 != ( renderBitDepth & adapter->D3DHWDeviceDesc.dwDeviceRenderBitDepth) )
InsertDisplayModeInListSorted(&adapter->hwDispModeList, &videoMode);
if( swRendererSupported )
InsertDisplayModeInListSorted(&adapter->swDispModeList, &videoMode);
return DDENUMRET_OK;
}
#endif // (DIRECT3D_VERSION < 0x900)
bool __cdecl WinVidInit() {
AppResultCode = 0;
#if (DIRECT3D_VERSION <= 0x700)
// NOTE: We can hack DirectDraw to support surface resolutions greater than 2048x2048.
// Not presented in the original game. Many thanks to Gemini-Loboto3 for this idea!
#if (DIRECT3D_VERSION == 0x700)
HMODULE hd3d = LoadLibrary("d3dim700.dll");
#else // (DIRECT3D_VERSION == 0x700)
HMODULE hd3d = LoadLibrary("d3dim.dll");
#endif // (DIRECT3D_VERSION == 0x700)
if( hd3d ) {
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNtHeader;
DWORD dwCodeBase;
DWORD dwCodeSize;
DWORD dwPatchBase;
DWORD dwOldProtect;
pDosHeader = (PIMAGE_DOS_HEADER)hd3d;
pNtHeader = (PIMAGE_NT_HEADERS)((char*)pDosHeader + pDosHeader->e_lfanew);
dwCodeBase = (DWORD)hd3d + pNtHeader->OptionalHeader.BaseOfCode;
dwCodeSize = pNtHeader->OptionalHeader.SizeOfCode;
static BYTE wantedBytes[] = {0xB8, 0x00, 0x08, 0x00, 0x00, 0x39};
dwPatchBase = (DWORD)memmem((void*)dwCodeBase, dwCodeSize, wantedBytes, sizeof(wantedBytes));
if( dwPatchBase ) {
dwPatchBase++;
VirtualProtect((LPVOID)dwPatchBase, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
*(DWORD*)dwPatchBase = ~0;
VirtualProtect((LPVOID)dwPatchBase, 4, dwOldProtect, &dwOldProtect);
}
}
#endif // (DIRECT3D_VERSION <= 0x700)
return ( WinVidRegisterGameWindowClass() &&
WinVidCreateGameWindow() &&
WinVidGetDisplayAdapters() &&
DisplayAdapterList.dwCount &&
WinVidGetDisplayModes() );
}
bool __cdecl WinVidGetDisplayAdapters() {
DISPLAY_ADAPTER_NODE *node, *nextNode;
for( node = DisplayAdapterList.head; node; node = nextNode ) {
nextNode = node->next;
DisplayModeListDelete(&node->body.swDispModeList);
DisplayModeListDelete(&node->body.hwDispModeList);
FlaggedStringDelete(&node->body.driverName);
FlaggedStringDelete(&node->body.driverDescription);
delete(node);
}
DisplayAdapterList.head = NULL;
DisplayAdapterList.tail = NULL;
DisplayAdapterList.dwCount = 0;
PrimaryDisplayAdapter = NULL;
if( !EnumerateDisplayAdapters(&DisplayAdapterList) )
return false;
#if (DIRECT3D_VERSION >= 0x900)
PrimaryDisplayAdapter = DisplayAdapterList.head;
return ( PrimaryDisplayAdapter != NULL );
#else // (DIRECT3D_VERSION >= 0x900)
for( node = DisplayAdapterList.head; node; node = node->next ) {
if( node->body.lpAdapterGuid == NULL ) { // Primary adapter GUID is NULL
PrimaryDisplayAdapter = node;
return true;
}
}
return false;
#endif // (DIRECT3D_VERSION >= 0x900)
}
void __thiscall FlaggedStringDelete(STRING_FLAGGED *item) {
if( item->isPresented && item->lpString ) {
delete[] item->lpString;
item->lpString = NULL;
item->isPresented = false;
}
}
bool __cdecl EnumerateDisplayAdapters(DISPLAY_ADAPTER_LIST *displayAdapterList) {
#if (DIRECT3D_VERSION >= 0x900)
bool d3dClean = false;
if( D3D == NULL ) {
if( !D3DCreate() ) return false;
d3dClean = true;
}
UINT num = D3D->GetAdapterCount();
for( UINT i = 0; i < num; ++i ) {
D3DADAPTER_IDENTIFIER9 id;
D3DCAPS9 caps;
if( FAILED(D3D->GetAdapterIdentifier(i, 0, &id)) || FAILED(D3D->GetDeviceCaps(i, D3DDEVTYPE_HAL, &caps)) ) {
continue;
}
DISPLAY_ADAPTER_NODE *listNode = new DISPLAY_ADAPTER_NODE;
if( listNode == NULL ) break;
listNode->next = NULL;
listNode->previous = displayAdapterList->tail;
FlaggedStringsCreate(&listNode->body);
DisplayModeListInit(&listNode->body.hwDispModeList);
DisplayModeListInit(&listNode->body.swDispModeList);
if( !displayAdapterList->head )
displayAdapterList->head = listNode;
if( displayAdapterList->tail )
displayAdapterList->tail->next = listNode;
displayAdapterList->tail = listNode;
displayAdapterList->dwCount++;
listNode->body.adapterGuid = id.DeviceIdentifier;
listNode->body.lpAdapterGuid = &listNode->body.adapterGuid;
lstrcpy(listNode->body.driverDescription.lpString, id.Description);
lstrcpy(listNode->body.driverName.lpString, id.DeviceName);
listNode->body.index = i;
listNode->body.caps = caps;
}
if( d3dClean ) D3DRelease();
return true;
#else // (DIRECT3D_VERSION >= 0x900)
return SUCCEEDED(DirectDrawEnumerate(EnumDisplayAdaptersCallback, (LPVOID)displayAdapterList));
#endif // (DIRECT3D_VERSION >= 0x900)
}
#if (DIRECT3D_VERSION < 0x900)
BOOL WINAPI EnumDisplayAdaptersCallback(GUID FAR *lpGUID, LPTSTR lpDriverDescription, LPTSTR lpDriverName, LPVOID lpContext) {
DDCAPS driverCaps;
DDCAPS helCaps;
DISPLAY_ADAPTER_NODE *listNode = new DISPLAY_ADAPTER_NODE;
DISPLAY_ADAPTER_LIST *adapterList = (DISPLAY_ADAPTER_LIST *)lpContext;
if( listNode == NULL || !DDrawCreate(lpGUID) )
return TRUE;
memset(&driverCaps, 0, sizeof(driverCaps));
memset(&helCaps, 0, sizeof(helCaps));
driverCaps.dwSize = sizeof(DDCAPS);
helCaps.dwSize = sizeof(DDCAPS);
if FAILED(DDraw->GetCaps(&driverCaps, &helCaps))
goto CLEANUP;
listNode->next = NULL;
listNode->previous = adapterList->tail;
FlaggedStringsCreate(&listNode->body);
DisplayModeListInit(&listNode->body.hwDispModeList);
DisplayModeListInit(&listNode->body.swDispModeList);
if( !adapterList->head )
adapterList->head = listNode;
if( adapterList->tail )
adapterList->tail->next = listNode;
adapterList->tail = listNode;
adapterList->dwCount++;
if( lpGUID ) {
// Any secondary adapter
listNode->body.adapterGuid = *lpGUID;
listNode->body.lpAdapterGuid = &listNode->body.adapterGuid;
} else {
// Primary adapter (GUID is NULL)
memset(&listNode->body.adapterGuid, 0, sizeof(GUID));
listNode->body.lpAdapterGuid = NULL;
}
lstrcpy(listNode->body.driverDescription.lpString, lpDriverDescription);
lstrcpy(listNode->body.driverName.lpString, lpDriverName);
listNode->body.driverCaps = driverCaps;
listNode->body.helCaps = helCaps;
listNode->body.screenWidth = 0;
listNode->body.hwRenderSupported = false;
listNode->body.swWindowedSupported = false;
listNode->body.hwWindowedSupported = false;
listNode->body.isVgaMode1Presented = false;
listNode->body.isVgaMode2Presented = false;
Enumerate3DDevices(&listNode->body);
CLEANUP :
DDrawRelease();
return TRUE;
}
#endif // (DIRECT3D_VERSION < 0x900)
void __thiscall FlaggedStringsCreate(DISPLAY_ADAPTER *adapter) {
LPTSTR lpDriverDescription = new char[256];
LPTSTR lpDriverName = new char[256];
if( lpDriverDescription ) {
*lpDriverDescription = 0;
adapter->driverDescription.lpString = lpDriverDescription;
adapter->driverDescription.isPresented = true;
}
if( lpDriverName ) {
*lpDriverName = 0;
adapter->driverName.lpString = lpDriverName;
adapter->driverName.isPresented = true;
}
}
bool __cdecl WinVidRegisterGameWindowClass() {
WNDCLASSEXA wndClass;
memset(&wndClass, 0, sizeof(wndClass));
wndClass.cbSize = sizeof(wndClass);
wndClass.style = 0;
wndClass.lpfnWndProc = WinVidGameWindowProc;
wndClass.hInstance = GameModule;
wndClass.hIcon = LoadIcon(GameModule, MAKEINTRESOURCE(IDI_MAINICON));
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.lpszClassName = GameClassName;
return ( RegisterClassEx(&wndClass) != 0 );
}
LRESULT CALLBACK WinVidGameWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
HDC hdc;
#if (DIRECT3D_VERSION < 0x900)
LPDDS surface;
#endif // (DIRECT3D_VERSION < 0x900)
HBRUSH hBrush;
PAINTSTRUCT paint;
#ifdef FEATURE_AUDIO_IMPROVED
static DWORD cdVolume = 0;
#endif // FEATURE_AUDIO_IMPROVED
if( IsFmvPlaying ) {
switch( Msg ) {
case WM_DESTROY :
IsGameWindowCreated = false;
HGameWindow = NULL;
PostQuitMessage(0);
break;
case WM_MOVE :
GameWindowPositionX = (int)(short)LOWORD(lParam);
GameWindowPositionY = (int)(short)HIWORD(lParam);
break;
case WM_ACTIVATEAPP :
IsGameWindowActive = ( wParam != 0 );
break;
case WM_SYSCOMMAND :
if( wParam == SC_KEYMENU ) return 0;
break;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
switch( Msg ) {
case WM_CREATE :
IsGameWindowCreated = true;
break;
case WM_DESTROY :
IsGameWindowCreated = false;
HGameWindow = NULL;
PostQuitMessage(0);
break;
case WM_MOVE :
GameWindowPositionX = (int)(short)LOWORD(lParam);
GameWindowPositionY = (int)(short)HIWORD(lParam);
break;
case WM_SIZE:
switch( wParam ) {
case SIZE_RESTORED :
IsGameWindowMinimized = false;
IsGameWindowMaximized = false;
break;
case SIZE_MAXIMIZED :
IsGameWindowMinimized = false;
IsGameWindowMaximized = true;
break;
case SIZE_MINIMIZED :
IsGameWindowMinimized = true;
IsGameWindowMaximized = false;
// fall through
default :
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
if( IsGameFullScreen ||
((int)(short)LOWORD(lParam) == GameWindowWidth && (int)(short)HIWORD(lParam) == GameWindowHeight) )
break;
GameWindowWidth = (int)(short)LOWORD(lParam);
GameWindowHeight = (int)(short)HIWORD(lParam);
if( IsGameWindowUpdating )
break;
UpdateGameResolution();
break;
case WM_PAINT :
hdc = BeginPaint(hWnd, &paint);
#if (DIRECT3D_VERSION >= 0x900)
if( IsGameFullScreen || D3DDev == NULL || FAILED(D3DDev->TestCooperativeLevel()) ) {
hBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
FillRect(hdc, &paint.rcPaint, hBrush);
} else {
D3DDev->Clear(0, NULL, D3DCLEAR_TARGET, 0, 1.0, 0);
D3DDev->Present(NULL, NULL, NULL, NULL);
}
#else // (DIRECT3D_VERSION >= 0x900)
surface = ( SavedAppSettings.RenderMode == RM_Software ) ? RenderBufferSurface : BackBufferSurface;
if( IsGameFullScreen || !PrimaryBufferSurface || !surface ) {
hBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
FillRect(hdc, &paint.rcPaint, hBrush);
}
else {
if( SavedAppSettings.RenderMode == RM_Software &&
!WinVidCheckGameWindowPalette(hWnd) &&
RenderBufferSurface )
{
WinVidClearBuffer(RenderBufferSurface, NULL, 0);
}
UpdateFrame(false, NULL);
}
#endif // (DIRECT3D_VERSION >= 0x900)
EndPaint(hWnd, &paint);
return 0;
case WM_ACTIVATE :
#if (DIRECT3D_VERSION < 0x900)
if( LOWORD(wParam) && DDrawPalette && PrimaryBufferSurface )
PrimaryBufferSurface->SetPalette(DDrawPalette);
#endif // (DIRECT3D_VERSION < 0x900)
break;
case WM_ERASEBKGND :
return 1;
case WM_ACTIVATEAPP :
#ifdef FEATURE_AUDIO_IMPROVED
// NOTE: If CD audio volume is set to zero, music is paused.
// To resume the music, the volume must be set to non zero value.
if( wParam && !IsGameWindowActive ) {
if( cdVolume ) {
S_CDVolume(cdVolume);
}
} else if ( !wParam && IsGameWindowActive ) {
cdVolume = S_GetCDVolume();
S_CDVolume(0);
}
#endif // FEATURE_AUDIO_IMPROVED
#ifdef FEATURE_INPUT_IMPROVED
if( !wParam && IsGameWindowActive ) {
JoyVibrationMute();
}
#endif // FEATURE_INPUT_IMPROVED
if( wParam && !IsGameWindowActive && IsGameFullScreen && SavedAppSettings.RenderMode == RM_Hardware )
WinVidNeedToResetBuffers = true;
IsGameWindowActive = ( wParam != 0 );
break;
case WM_SETCURSOR :
if( IsGameFullScreen ) {
SetCursor(NULL);
return 1;
}
break;
case WM_GETMINMAXINFO :
if( WinVidGetMinMaxInfo((LPMINMAXINFO)lParam) )
return 0;
break;
case WM_NCPAINT :
case WM_NCLBUTTONDOWN :
case WM_NCLBUTTONDBLCLK :
case WM_NCRBUTTONDOWN :
case WM_NCRBUTTONDBLCLK :
case WM_NCMBUTTONDOWN :
case WM_NCMBUTTONDBLCLK :
if( IsGameFullScreen ) return 0;
break;
case WM_SYSCOMMAND :
if( wParam == SC_KEYMENU ) return 0;
break;
case WM_SIZING :
WinVidResizeGameWindow(hWnd, wParam, (LPRECT)lParam);
break;
case WM_MOVING :
if( IsGameFullScreen || IsGameWindowMaximized ) {
GetWindowRect(hWnd, (LPRECT)lParam);
return 1;
}
break;
case WM_ENTERSIZEMOVE :
IsGameWindowChanging = true;
break;
case WM_EXITSIZEMOVE :
IsGameWindowChanging = false;
break;
#if (DIRECT3D_VERSION < 0x900)
case WM_PALETTECHANGED :
if( hWnd != (HWND)wParam && !IsGameFullScreen && DDrawPalette )
InvalidateRect(hWnd, NULL, FALSE);
break;
#endif // (DIRECT3D_VERSION < 0x900)
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
void __cdecl WinVidResizeGameWindow(HWND hWnd, int edge, LPRECT rect) {
bool isShiftPressed;
int width, height;
if( IsGameFullScreen ) {
rect->left = 0;
rect->top = 0;
rect->right = FullScreenWidth;
rect->bottom = FullScreenHeight;
}
isShiftPressed = ( GetAsyncKeyState(VK_SHIFT) < 0 );
width = rect->right - rect->left;
height = rect->bottom - rect->top;
GameWindowCalculateSizeFromClientByZero(&width, &height);
if( edge == WMSZ_TOP || edge == WMSZ_BOTTOM ) {
if( isShiftPressed )
height &= ~0x1F;
width = CalculateWindowWidth(width, height);
} else {
if( isShiftPressed )
width &= ~0x1F;
height = CalculateWindowHeight(width, height);
}
if( IsMinWindowSizeSet ) {
if( width < MinWindowClientWidth )
width = MinWindowClientWidth;
if( height < MinWindowClientHeight )
height = MinWindowClientHeight;
}
if( IsMaxWindowSizeSet ) {
if( width > MaxWindowClientWidth )
width = MaxWindowClientWidth;
if( height > MaxWindowClientHeight )
height = MaxWindowClientHeight;
}
GameWindowCalculateSizeFromClient(&width, &height);
switch( edge ) {
case WMSZ_TOPLEFT :
rect->left = rect->right - width;
rect->top = rect->bottom - height;
break;
case WMSZ_RIGHT :
case WMSZ_BOTTOM :
case WMSZ_BOTTOMRIGHT :
rect->right = rect->left + width;
rect->bottom = rect->top + height;
break;
case WMSZ_LEFT :
case WMSZ_BOTTOMLEFT :
rect->left = rect->right - width;
rect->bottom = rect->top + height;
break;
case WMSZ_TOP :
case WMSZ_TOPRIGHT :
rect->right = rect->left + width;
rect->top = rect->bottom - height;
break;
}
}
bool __cdecl WinVidCheckGameWindowPalette(HWND hWnd) {
HDC hdc;
PALETTEENTRY sysPalette[256];
RGB888 bufPalette[256];
hdc = GetDC(hWnd);
if( hdc == NULL )
return false;
GetSystemPaletteEntries(hdc, 0, 256, sysPalette);
ReleaseDC(hWnd, hdc);
for( int i=0; i < 256; ++i ) {
bufPalette[i].red = sysPalette[i].peRed;
bufPalette[i].green = sysPalette[i].peGreen;
bufPalette[i].blue = sysPalette[i].peBlue;
}
return ( !memcmp(bufPalette, GamePalette8, sizeof(bufPalette)) );
}
bool __cdecl WinVidCreateGameWindow() {
RECT rect;
IsGameWindowActive = true;
IsGameWindowShow = true;
IsDDrawGameWindowShow = false;
IsMessageLoopClosed = false;
IsGameWindowUpdating = false;
IsGameWindowMinimized = false;
IsGameWindowMaximized = false;
IsGameWindowCreated = false;
IsGameFullScreen = false;
IsMinMaxInfoSpecial = false;
IsGameWindowChanging = false;
WinVidClearMinWindowSize();
WinVidClearMaxWindowSize();
HGameWindow = CreateWindowEx(WS_EX_APPWINDOW, GameClassName, GameWindowName, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, GameModule, NULL);
if( HGameWindow == NULL)
return false;
GetWindowRect(HGameWindow, &rect);
GameWindow_X = rect.left;
GameWindow_Y = rect.top;
WinVidHideGameWindow();
return true;
}
void __cdecl WinVidFreeWindow() {
WinVidExitMessage();
UnregisterClass(GameClassName, GameModule);
}
void __cdecl WinVidExitMessage() {
if( HGameWindow && IsWindow(HGameWindow) ) {
PostMessage(HGameWindow, WM_CLOSE, 0, 0);
while( WinVidSpinMessageLoop(false) ) /* just wait */;
HGameWindow = NULL;
}
}
DISPLAY_ADAPTER_NODE *__cdecl WinVidGetDisplayAdapter(GUID *lpGuid) {
DISPLAY_ADAPTER_NODE *adapter;
if( lpGuid != NULL ) {
for( adapter = DisplayAdapterList.head; adapter; adapter = adapter->next ) {
if( !memcmp(&adapter->body.adapterGuid, lpGuid, sizeof(GUID)) )
return adapter;
}
}
return PrimaryDisplayAdapter;
}
void __cdecl WinVidStart() {
if( SavedAppSettings.PreferredDisplayAdapter == NULL )
#if (DIRECT3D_VERSION >= 0x900)
throw ERR_CantInitRenderer;
#else // (DIRECT3D_VERSION >= 0x900)
throw ERR_CantCreateDirectDraw;
#endif // (DIRECT3D_VERSION >= 0x900)
DISPLAY_ADAPTER *preferred = &SavedAppSettings.PreferredDisplayAdapter->body;
CurrentDisplayAdapter = *preferred;
FlaggedStringCopy(&CurrentDisplayAdapter.driverDescription, &preferred->driverDescription);
FlaggedStringCopy(&CurrentDisplayAdapter.driverName, &preferred->driverName);
DisplayModeListInit(&CurrentDisplayAdapter.hwDispModeList);
DisplayModeListCopy(&CurrentDisplayAdapter.hwDispModeList, &preferred->hwDispModeList);
DisplayModeListInit(&CurrentDisplayAdapter.swDispModeList);
DisplayModeListCopy(&CurrentDisplayAdapter.swDispModeList, &preferred->swDispModeList);
#if (DIRECT3D_VERSION < 0x900)
if( !DDrawCreate(CurrentDisplayAdapter.lpAdapterGuid) )
throw ERR_CantCreateDirectDraw;
#endif // (DIRECT3D_VERSION < 0x900)
}
void __cdecl WinVidFinish() {
#if (DIRECT3D_VERSION < 0x900)
if( IsDDrawGameWindowShow )
HideDDrawGameWindow();
DDrawRelease();
#endif // (DIRECT3D_VERSION < 0x900)
}
void __thiscall DisplayModeListInit(DISPLAY_MODE_LIST *pList) {
pList->head = NULL;
pList->tail = NULL;
pList->dwCount = 0;
}
void __thiscall DisplayModeListDelete(DISPLAY_MODE_LIST *pList) {
DISPLAY_MODE_NODE *node;
DISPLAY_MODE_NODE *nextNode;
for( node = pList->head; node; node = nextNode ) {
nextNode = node->next;
delete(node);
}
DisplayModeListInit(pList);
}
DISPLAY_MODE *__thiscall InsertDisplayMode(DISPLAY_MODE_LIST *modeList, DISPLAY_MODE_NODE *before) {
if( !before || !before->previous )
return InsertDisplayModeInListHead(modeList);
DISPLAY_MODE_NODE *node = new DISPLAY_MODE_NODE;
if( !node )
return NULL;
before->previous->next = node;
node->previous = before->previous;
before->previous = node;
node->next = before;
modeList->dwCount++;
return &node->body;
}
DISPLAY_MODE *__thiscall InsertDisplayModeInListHead(DISPLAY_MODE_LIST *modeList) {
DISPLAY_MODE_NODE *node = new DISPLAY_MODE_NODE;
if( !node )
return NULL;
node->next = modeList->head;
node->previous = NULL;
if( modeList->head )
modeList->head->previous = node;
if( !modeList->tail )
modeList->tail = node;
modeList->head = node;
modeList->dwCount++;
return &node->body;
}
DISPLAY_MODE *__thiscall InsertDisplayModeInListTail(DISPLAY_MODE_LIST *modeList) {
DISPLAY_MODE_NODE *node = new DISPLAY_MODE_NODE;
if( !node )
return NULL;
node->next = NULL;
node->previous = modeList->tail;
if( modeList->tail )
modeList->tail->next = node;
if( !modeList->head )
modeList->head = node;
modeList->tail = node;
modeList->dwCount++;
return &node->body;
}
/*
* Inject function
*/
void Inject_WinVid() {
#if (DIRECT3D_VERSION < 0x900)
INJECT(0x00444C80, DDrawCreate);
INJECT(0x00444CE0, DDrawRelease);
#endif // (DIRECT3D_VERSION < 0x900)
INJECT(0x00444D20, GameWindowCalculateSizeFromClient);
INJECT(0x00444DA0, GameWindowCalculateSizeFromClientByZero);
INJECT(0x00444E10, WinVidSetMinWindowSize);
INJECT(0x00444E60, WinVidClearMinWindowSize);
INJECT(0x00444E70, WinVidSetMaxWindowSize);
INJECT(0x00444EC0, WinVidClearMaxWindowSize);
INJECT(0x00444ED0, CalculateWindowWidth);
INJECT(0x00444F20, CalculateWindowHeight);
INJECT(0x00444F50, WinVidGetMinMaxInfo);
INJECT(0x00445060, WinVidFindGameWindow);
INJECT(0x00445080, WinVidSpinMessageLoop);
INJECT(0x00445170, WinVidShowGameWindow);
INJECT(0x004451C0, WinVidHideGameWindow);
INJECT(0x00445200, WinVidSetGameWindowSize);
#if (DIRECT3D_VERSION < 0x900)
INJECT(0x00445240, ShowDDrawGameWindow);
INJECT(0x004452F0, HideDDrawGameWindow);
INJECT(0x00445380, DDrawSurfaceCreate);
INJECT(0x004453D0, DDrawSurfaceRestoreLost);
INJECT(0x00445420, WinVidClearBuffer);
INJECT(0x00445470, WinVidBufferLock);
INJECT(0x004454B0, WinVidBufferUnlock);
INJECT(0x004454E0, WinVidCopyBitmapToBuffer);
INJECT(0x00445570, GetRenderBitDepth);
INJECT(0x00445600, WinVidGetColorBitMasks);
INJECT(0x00445680, BitMaskGetNumberOfBits);
INJECT(0x004456D0, CalculateCompatibleColor);
#endif // (DIRECT3D_VERSION < 0x900)
INJECT(0x00445740, WinVidGetDisplayMode);
INJECT(0x004457D0, WinVidGoFullScreen);
INJECT(0x00445860, WinVidGoWindowed);
INJECT(0x00445970, WinVidSetDisplayAdapter);
INJECT(0x00445A50, CompareVideoModes);
INJECT(0x00445AA0, WinVidGetDisplayModes);
#if (DIRECT3D_VERSION < 0x900)
INJECT(0x00445B00, EnumDisplayModesCallback);
#endif // (DIRECT3D_VERSION < 0x900)
INJECT(0x00445EC0, WinVidInit);
INJECT(0x00445F00, WinVidGetDisplayAdapters);
INJECT(0x00445FB0, FlaggedStringDelete);
INJECT(0x00445FD0, EnumerateDisplayAdapters);
#if (DIRECT3D_VERSION < 0x900)
INJECT(0x00445FF0, EnumDisplayAdaptersCallback);
#endif // (DIRECT3D_VERSION < 0x900)
INJECT(0x004461B0, FlaggedStringsCreate);
INJECT(0x004461F0, WinVidRegisterGameWindowClass);
INJECT(0x00446260, WinVidGameWindowProc);
INJECT(0x00446870, WinVidResizeGameWindow);
INJECT(0x00446A50, WinVidCheckGameWindowPalette);
INJECT(0x00446B10, WinVidCreateGameWindow);
INJECT(0x00446BE0, WinVidFreeWindow);
INJECT(0x00446C10, WinVidExitMessage);
INJECT(0x00446C60, WinVidGetDisplayAdapter);
INJECT(0x00446CB0, WinVidStart);
INJECT(0x00447030, WinVidFinish);
INJECT(0x00447050, DisplayModeListInit);
INJECT(0x00447060, DisplayModeListDelete);
// INJECT(0x004470A0, InsertDisplayMode); // NOTE: new one is not compatible anymore (original one is junk!!!)
INJECT(0x004470C0, InsertDisplayModeInListHead);
INJECT(0x00447110, InsertDisplayModeInListTail);
}
================================================
FILE: specific/winvid.h
================================================
/*
* Copyright (c) 2017-2020 Michael Chaban. All rights reserved.
* Original game is created by Core Design Ltd. in 1997.
* Lara Croft and Tomb Raider are trademarks of Embracer Group AB.
*
* This file is part of TR2Main.
*
* TR2Main is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TR2Main is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TR2Main. If not, see .
*/
#ifndef WINVID_H_INCLUDED
#define WINVID_H_INCLUDED
#include "global/types.h"
/*
* Function list
*/
#if (DIRECT3D_VERSION < 0x900)
bool __cdecl DDrawCreate(LPGUID lpGUID); // 0x00444C80
void __cdecl DDrawRelease(); // 0x00444CE0
#endif // (DIRECT3D_VERSION < 0x900)
void __cdecl GameWindowCalculateSizeFromClient(int *width, int *height); // 0x00444D20
void __cdecl GameWindowCalculateSizeFromClientByZero(int *width, int *height); // 0x00444DA0
void __cdecl WinVidSetMinWindowSize(int width, int height); // 0x00444E10
void __cdecl WinVidClearMinWindowSize(); // 0x00444E60
void __cdecl WinVidSetMaxWindowSize(int width, int height); // 0x00444E70
void __cdecl WinVidClearMaxWindowSize(); // 0x00444EC0
int __cdecl CalculateWindowWidth(int width, int height); // 0x00444ED0
int __cdecl CalculateWindowHeight(int width, int height); // 0x00444F20
bool __cdecl WinVidGetMinMaxInfo(LPMINMAXINFO info); // 0x00444F50
HWND __cdecl WinVidFindGameWindow(); // 0x00445060
bool __cdecl WinVidSpinMessageLoop(bool needWait); // 0x00445080
void __cdecl WinVidShowGameWindow(int nCmdShow); // 0x00445170
void __cdecl WinVidHideGameWindow(); // 0x004451C0
void __cdecl WinVidSetGameWindowSize(int width, int height); // 0x00445200
#if (DIRECT3D_VERSION < 0x900)
bool __cdecl ShowDDrawGameWindow(bool active); // 0x00445240
bool __cdecl HideDDrawGameWindow(); // 0x004452F0
HRESULT __cdecl DDrawSurfaceCreate(LPDDSDESC dsp, LPDDS *surface); // 0x00445380
HRESULT __cdecl DDrawSurfaceRestoreLost(LPDDS surface1, LPDDS surface2, bool blank); // 0x004453D0
bool __cdecl WinVidClearBuffer(LPDDS surface, LPRECT rect, DWORD fillColor); // 0x00445420
HRESULT __cdecl WinVidBufferLock(LPDDS surface, LPDDSDESC desc, DWORD flags); // 0x00445470
HRESULT __cdecl WinVidBufferUnlock(LPDDS surface, LPDDSDESC desc); // 0x004454B0
bool __cdecl WinVidCopyBitmapToBuffer(LPDDS surface, BYTE *bitmap); // 0x004454E0
DWORD __cdecl GetRenderBitDepth(DWORD dwRGBBitCount); // 0x00445570
void __thiscall WinVidGetColorBitMasks(COLOR_BIT_MASKS *bm, LPDDPIXELFORMAT pixelFormat); // 0x00445600
void __cdecl BitMaskGetNumberOfBits(DWORD bitMask, DWORD *bitDepth, DWORD *bitOffset); // 0x00445680
DWORD __cdecl CalculateCompatibleColor(COLOR_BIT_MASKS *mask, int red, int green, int blue, int alpha); // 0x004456D0
#endif // (DIRECT3D_VERSION < 0x900)
bool __cdecl WinVidGetDisplayMode(DISPLAY_MODE *dispMode); // 0x00445740
bool __cdecl WinVidGoFullScreen(DISPLAY_MODE *dispMode); // 0x004457D0
bool __cdecl WinVidGoWindowed(int width, int height, DISPLAY_MODE *dispMode); // 0x00445860
void __cdecl WinVidSetDisplayAdapter(DISPLAY_ADAPTER *dispAdapter); // 0x00445970
bool __thiscall CompareVideoModes(DISPLAY_MODE *mode1, DISPLAY_MODE *mode2); // 0x00445A50
bool __cdecl WinVidGetDisplayModes(); // 0x00445AA0
#if (DIRECT3D_VERSION < 0x900)
HRESULT WINAPI EnumDisplayModesCallback(LPDDSDESC lpDDSurfaceDesc, LPVOID lpContext); // 0x00445B00
#endif // (DIRECT3D_VERSION < 0x900)
bool __cdecl WinVidInit(); // 0x00445EC0
bool __cdecl WinVidGetDisplayAdapters(); // 0x00445F00
void __thiscall FlaggedStringDelete(STRING_FLAGGED *item); // 0x00445FB0
bool __cdecl EnumerateDisplayAdapters(DISPLAY_ADAPTER_LIST *displayAdapterList); // 0x00445FD0
#if (DIRECT3D_VERSION < 0x900)
BOOL WINAPI EnumDisplayAdaptersCallback(GUID FAR *lpGUID, LPTSTR lpDriverDescription, LPTSTR lpDriverName, LPVOID lpContext); // 0x00445FF0
#endif // (DIRECT3D_VERSION < 0x900)
void __thiscall FlaggedStringsCreate(DISPLAY_ADAPTER *adapter); // 0x004461B0
bool __cdecl WinVidRegisterGameWindowClass(); // 0x004461F0
LRESULT CALLBACK WinVidGameWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); // 0x00446260
void __cdecl WinVidResizeGameWindow(HWND hWnd, int edge, LPRECT rect); // 0x00446870
bool __cdecl WinVidCheckGameWindowPalette(HWND hWnd); // 0x00446A50
bool __cdecl WinVidCreateGameWindow(); // 0x00446B10
void __cdecl WinVidFreeWindow(); // 0x00446BE0
void __cdecl WinVidExitMessage(); // 0x00446C10
DISPLAY_ADAPTER_NODE *__cdecl WinVidGetDisplayAdapter(GUID *lpGuid); // 0x00446C60
void __cdecl WinVidStart(); // 0x00446CB0
void __cdecl WinVidFinish(); // 0x00447030
void __thiscall DisplayModeListInit(DISPLAY_MODE_LIST *pList); // 0x00447050
void __thiscall DisplayModeListDelete(DISPLAY_MODE_LIST *pList); // 0x00447060
DISPLAY_MODE *__thiscall InsertDisplayMode(DISPLAY_MODE_LIST *modeList, DISPLAY_MODE_NODE *before); // 0x004470A0
DISPLAY_MODE *__thiscall InsertDisplayModeInListHead(DISPLAY_MODE_LIST *modeList); // 0x004470C0
DISPLAY_MODE *__thiscall InsertDisplayModeInListTail(DISPLAY_MODE_LIST *modeList); // 0x00447110
#endif // WINVID_H_INCLUDED