Adds original 3dfx rendering path (#434)

* using dossys, virtual_fb driver

* keyboard and hires working with dossys

* adds explicit opengl mode, opengles support, use-after-free fix

* remove compiler warnings

* bump BRender dep to latest commit

* adds mouse code from DOS version which correctly scales based on pixel size

* more 3dfx fixes, adds game-completed arg
This commit is contained in:
Dethrace Engineering Department 2025-03-31 18:44:38 +13:00 committed by GitHub
parent 1ef129a7bf
commit ca1ef76e7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 2926 additions and 540 deletions

View File

@ -16,7 +16,7 @@ jobs:
matrix:
platform:
- { name: 'Linux', arch: 'x64', os: ubuntu-latest, werror: true }
- { name: 'Linux', arch: 'arm64', os: ubuntu-latest, werror: true, cmake-toolchain-file: 'cmake/toolchains/linux-aarch64.cmake', apt-packages: 'gcc-aarch64-linux-gnu g++-aarch64-linux-gnu', cross: true }
- { name: 'Linux', arch: 'arm64', os: ubuntu-24.04-arm, werror: true }
- { name: 'MacOS', arch: 'arm64-x64', os: macos-latest, werror: true, cmake-args: '-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"' }
- { name: 'Windows', arch: 'x86', os: windows-latest, msvc-arch: 'Win32' }
- { name: 'Windows', arch: 'x64', os: windows-latest, msvc-arch: 'x64' }
@ -45,12 +45,13 @@ jobs:
- name: Set up SDL
uses: libsdl-org/setup-sdl@main
with:
add-to-environment: true
build-type: Release
cmake-arguments: ${{ matrix.platform.cmake-args }}
cmake-generator: Ninja
cmake-toolchain-file: ${{ matrix.platform.cmake-toolchain-file }}
discriminator: ${{ matrix.platform.arch }}
version: 2-latest
add-to-environment: true
- name: 'Prepare sources for release'
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: |

View File

@ -37,6 +37,7 @@ option(BUILD_TESTS "Build unit tests." OFF)
option(DETHRACE_INSTALL "Add install target" OFF)
option(DETHRACE_WERROR "Treat warnings as errors")
option(DETHRACE_FIX_BUGS "Fix Dethrace bugs" ON)
option(DETHRACE_3DFX_PATCH "Include changes from VOODOO2C.EXE" ON)
function(add_compile_flag_if_supported TARGET FLAG)
cmake_parse_arguments(ARGS "" "" "LANGUAGES" ${ARGN})
@ -47,6 +48,7 @@ function(add_compile_flag_if_supported TARGET FLAG)
set(HAVE_FLAG_VARIABLE_NAME "HAVE_${FLAG_TO_IDENTIFIER}")
check_c_compiler_flag("${FLAG}" "${HAVE_FLAG_VARIABLE_NAME}")
if(${HAVE_FLAG_VARIABLE_NAME})
string(REPLACE ";" "," ARGS_LANGUAGES "${ARGS_LANGUAGES}")
target_compile_options("${TARGET}" PRIVATE "$<$<COMPILE_LANGUAGE:${ARGS_LANGUAGES}>:${FLAG}>")
endif()
endfunction()

View File

@ -1,4 +1,4 @@
# Dethrace
# Dethrace
[![Workflow](https://github.com/dethrace-labs/dethrace/actions/workflows/workflow.yaml/badge.svg)](https://github.com/dethrace-labs/dethrace/actions/workflows/workflow.yml)
[![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/dethrace_labs)
@ -9,8 +9,8 @@ Dethrace is an attempt to learn how the 1997 driving/mayhem game [Carmageddon](h
## Progress
(Follow us on Discord or Twitter to get notified of updates!)
#### Last updated June 17 2024
- 92% of functions implemented
#### Last updated March 2025
- 94% of functions implemented
- Latest screenshot:
<img width="752" alt="Screenshot 2024-05-27 at 8 44 10AM" src="https://github.com/dethrace-labs/dethrace/assets/1063652/10b3b579-1eb1-4242-8b56-ff062cfff810">

@ -1 +1 @@
Subproject commit 9c34086300f4f0bbb3a55206380f25b17dad6c12
Subproject commit ee344356e359d9b9b310dda08e3be672da9b7f01

View File

@ -40,6 +40,9 @@ target_compile_definitions(dethrace_obj PRIVATE INSIDE_DETHRACE)
if(DETHRACE_FIX_BUGS)
target_compile_definitions(dethrace_obj PRIVATE DETHRACE_FIX_BUGS)
endif()
if(DETHRACE_3DFX_PATCH)
target_compile_definitions(dethrace_obj PRIVATE DETHRACE_3DFX_PATCH)
endif()
if(IS_BIGENDIAN)
target_compile_definitions(dethrace_obj PRIVATE BR_ENDIAN_BIG=1)

View File

@ -994,6 +994,7 @@ void FinishCars(tU32 pLast_frame_time, tU32 pTime) {
BrVector3SetFloat(&minus_k, 0.f, 0.f, -1.f);
}
BrMatrix34ApplyV(&car->direction, &minus_k, &car->car_master_actor->t.t.mat);
} else if (gLast_mechanics_time > pLast_frame_time && gCar_to_view == car) {
BrVector3Sub(&car->old_v, &car->old_v, &car->v);
BrVector3Scale(&car->old_v, &car->old_v, (gLast_mechanics_time - pLast_frame_time) / harness_game_config.physics_step_time);
@ -2591,7 +2592,7 @@ void CalcForce(tCar_spec* c, br_scalar dt) {
ApplyTorque(c, &rightplane);
BrVector3Scale(&rightplane, &b, dt / c->M);
BrVector3Accumulate(&c->v, &rightplane);
if (c->speed < 0.000099999997
if (c->speed < 0.0001f
&& ((!c->keys.acc && c->joystick.acc <= 0) || !c->gear)
&& !c->keys.dec
&& c->joystick.dec <= 0
@ -5243,6 +5244,7 @@ void NormalPositionExternalCamera(tCar_spec* c, tU32 pTime) {
manual_swing = gOld_yaw__car != gCamera_yaw || swoop;
manual_zoom = (double)gOld_zoom != gCamera_zoom;
BrVector3Copy(&old_camera_pos, &gCamera->t.t.translate.t);
if (!gProgram_state.cockpit_on) {
if (swoop) {
gCamera_yaw = 0;
@ -7517,5 +7519,6 @@ int GetPrecalculatedFacesUnderCar(tCar_spec* pCar, tFace_ref** pFace_refs) {
// IDA: br_material* __cdecl SomeNearbyMaterial()
br_material* SomeNearbyMaterial(void) {
LOG_TRACE("()");
NOT_IMPLEMENTED();
return gFace_list__car[gProgram_state.current_car.box_face_start].material;
}

View File

@ -4,11 +4,14 @@
#include "displays.h"
#include "errors.h"
#include "globvars.h"
#include "globvrbm.h"
#include "globvrkm.h"
#include "globvrpb.h"
#include "harness/hooks.h"
#include "harness/trace.h"
#include "init.h"
#include "pd/sys.h"
#include "pedestrn.h"
#include "replay.h"
#include "spark.h"
#include "trig.h"
@ -99,7 +102,8 @@ br_scalar CalculateWrappingMultiplier(br_scalar pValue, br_scalar pYon) {
// IDA: br_scalar __usercall DepthCueingShiftToDistance@<ST0>(int pShift@<EAX>)
br_scalar DepthCueingShiftToDistance(int pShift) {
LOG_TRACE("(%d)", pShift);
NOT_IMPLEMENTED();
return powf(10.0f, pShift * 0.1f) * gCamera_yon;
}
// IDA: void __usercall FogAccordingToGPSCDE(br_material *pMaterial@<EAX>)
@ -107,7 +111,29 @@ void FogAccordingToGPSCDE(br_material* pMaterial) {
int start;
int end;
LOG_TRACE("(%p)", pMaterial);
NOT_IMPLEMENTED();
start = gProgram_state.current_depth_effect.start;
end = gProgram_state.current_depth_effect.end;
switch (gProgram_state.current_depth_effect.type) {
case eDepth_effect_darkness:
pMaterial->fog_min = DepthCueingShiftToDistance(-start);
pMaterial->fog_colour = BR_COLOUR_RGB(0, 0, 0);
pMaterial->flags |= BR_MATF_FOG_LOCAL;
pMaterial->fog_max = DepthCueingShiftToDistance(end);
break;
case eDepth_effect_fog:
pMaterial->fog_min = DepthCueingShiftToDistance(-start);
pMaterial->fog_colour = BR_COLOUR_RGB(248, 248, 248);
pMaterial->flags |= BR_MATF_FOG_LOCAL;
pMaterial->fog_max = DepthCueingShiftToDistance(end);
break;
case eDepth_effect_none:
pMaterial->flags &= ~BR_MATF_FOG_LOCAL;
break;
}
BrMaterialUpdate(pMaterial, BR_MATU_ALL);
}
// IDA: void __cdecl FrobFog()
@ -115,7 +141,27 @@ void FrobFog(void) {
int i;
br_material* mat;
LOG_TRACE("()");
NOT_IMPLEMENTED();
if (gTrack_actor) {
ProcessMaterials(gTrack_actor, (tPMFM2CB*)FogAccordingToGPSCDE);
}
if (gNon_track_actor) {
ProcessMaterials(gNon_track_actor, (tPMFM2CB*)FogAccordingToGPSCDE);
}
for (i = 0; i < COUNT_OF(gMaterial); i++) {
mat = gMaterial[i];
FogAccordingToGPSCDE(mat);
}
for (i = 0; i < COUNT_OF(gCurrent_race.material_modifiers); i++) {
mat = gCurrent_race.material_modifiers[i].skid_mark_material;
if (mat) {
FogAccordingToGPSCDE(mat);
}
}
FogAccordingToGPSCDE(gDefault_track_material);
if (gPed_material) {
FogAccordingToGPSCDE(gPed_material);
}
}
// IDA: void __usercall InstantDepthChange(tDepth_effect_type pType@<EAX>, br_pixelmap *pSky_texture@<EDX>, int pStart@<EBX>, int pEnd@<ECX>)
@ -136,6 +182,12 @@ void InstantDepthChange(tDepth_effect_type pType, br_pixelmap* pSky_texture, int
gProgram_state.default_depth_effect.type = pType;
gProgram_state.default_depth_effect.start = pStart;
gProgram_state.default_depth_effect.end = pEnd;
#ifdef DETHRACE_3DFX_PATCH
if (gMaterial_fogging) {
FrobFog();
}
#endif
}
// IDA: br_scalar __cdecl Tan(br_scalar pAngle)
@ -338,14 +390,19 @@ void InitDepthEffects(void) {
if (gHorizon_material == NULL) {
FatalError(kFatalError_FindSkyMaterial_S, "HORIZON.MAT"); // 2nd argument added
}
gHorizon_material->index_blend = BrPixelmapAllocate(BR_PMT_INDEX_8, 256, 256, NULL, 0);
BrTableAdd(gHorizon_material->index_blend);
for (i = 0; i < 256; i++) {
for (j = 0; j < 256; j++) {
*((tU8*)gHorizon_material->index_blend->pixels + 256 * i + j) = j;
#ifdef DETHRACE_3DFX_PATCH
if (gScreen->type == BR_PMT_INDEX_8 && !gMaterial_fogging)
#endif
{
gHorizon_material->index_blend = BrPixelmapAllocate(BR_PMT_INDEX_8, 256, 256, NULL, 0);
BrTableAdd(gHorizon_material->index_blend);
for (i = 0; i < 256; i++) {
for (j = 0; j < 256; j++) {
*((tU8*)gHorizon_material->index_blend->pixels + 256 * i + j) = j;
}
}
gHorizon_material->flags |= BR_MATF_PERSPECTIVE;
}
gHorizon_material->flags |= BR_MATF_PERSPECTIVE;
BrMaterialAdd(gHorizon_material);
gForward_sky_model = CreateHorizonModel(gCamera);
gRearview_sky_model = CreateHorizonModel(gRearview_camera);
@ -520,9 +577,15 @@ void DoHorizon(br_pixelmap* pRender_buffer, br_pixelmap* pDepth_buffer, br_actor
LOG_TRACE("(%p, %p, %p, %p)", pRender_buffer, pDepth_buffer, pCamera, pCamera_to_world);
yaw = BrRadianToAngle(atan2f(pCamera_to_world->m[2][0], pCamera_to_world->m[2][2]));
if (!gProgram_state.cockpit_on && !(gAction_replay_mode && gAction_replay_camera_mode)) {
if (!gProgram_state.cockpit_on && !gAction_replay_mode && gAction_replay_camera_mode != eAction_replay_standard
#ifdef DETHRACE_3DFX_PATCH
&& !gBlitting_is_slow
#endif
) {
return;
}
if (gRendering_mirror) {
actor = gRearview_sky_actor;
} else {
@ -577,6 +640,11 @@ void DoFog(br_pixelmap* pRender_buffer, br_pixelmap* pDepth_buffer) {
void DepthEffect(br_pixelmap* pRender_buffer, br_pixelmap* pDepth_buffer, br_actor* pCamera, br_matrix34* pCamera_to_world) {
LOG_TRACE("(%p, %p, %p, %p)", pRender_buffer, pDepth_buffer, pCamera, pCamera_to_world);
#ifdef DETHRACE_3DFX_PATCH
if (gMaterial_fogging) {
return;
}
#endif
if (gProgram_state.current_depth_effect.type == eDepth_effect_darkness) {
DoDepthCue(pRender_buffer, pDepth_buffer);
}

View File

@ -3,8 +3,10 @@
#include "constants.h"
#include "controls.h"
#include "depth.h"
#include "errors.h"
#include "flicplay.h"
#include "globvars.h"
#include "globvrbm.h"
#include "globvrkm.h"
#include "globvrpb.h"
#include "grafdata.h"
@ -230,7 +232,25 @@ void DRPixelmapCleverText2(br_pixelmap* pPixelmap, int pX, int pY, tDR_font* pFo
// IDA: void __usercall DeviouslyDimRectangle(br_pixelmap *pPixelmap@<EAX>, int pLeft@<EDX>, int pTop@<EBX>, int pRight@<ECX>, int pBottom, int pKnock_out_corners)
void DeviouslyDimRectangle(br_pixelmap* pPixelmap, int pLeft, int pTop, int pRight, int pBottom, int pKnock_out_corners) {
LOG_TRACE("(%p, %d, %d, %d, %d, %d)", pPixelmap, pLeft, pTop, pRight, pBottom, pKnock_out_corners);
NOT_IMPLEMENTED();
if (pPixelmap != gBack_screen) {
FatalError(124);
}
gDim_model->vertices[1].p.v[0] = pLeft;
gDim_model->vertices[0].p.v[0] = pLeft;
gDim_model->vertices[3].p.v[0] = pRight;
gDim_model->vertices[2].p.v[0] = pRight;
gDim_model->vertices[3].p.v[1] = -pTop;
gDim_model->vertices[0].p.v[1] = -pTop;
gDim_model->vertices[2].p.v[1] = -pBottom;
gDim_model->vertices[1].p.v[1] = -pBottom;
BrModelUpdate(gDim_model, BR_MODU_VERTEX_POSITIONS);
gDim_actor->render_style = BR_RSTYLE_FACES;
PDUnlockRealBackScreen(1);
BrZbSceneRender(g2d_camera, g2d_camera, gBack_screen, gDepth_buffer);
PDLockRealBackScreen(1);
gDim_actor->render_style = BR_RSTYLE_NONE;
}
// IDA: void __cdecl DimRectangle(br_pixelmap *pPixelmap, int pLeft, int pTop, int pRight, int pBottom, int pKnock_out_corners)
@ -244,6 +264,11 @@ void DimRectangle(br_pixelmap* pPixelmap, int pLeft, int pTop, int pRight, int p
int width;
LOG_TRACE9("(%p, %d, %d, %d, %d, %d)", pPixelmap, pLeft, pTop, pRight, pBottom, pKnock_out_corners);
if (gDevious_2d) {
DeviouslyDimRectangle(pPixelmap, pLeft, pTop, pRight, pBottom, pKnock_out_corners);
return;
}
ptr = (tU8*)pPixelmap->pixels + pLeft + pPixelmap->row_bytes * pTop;
line_skip = pPixelmap->row_bytes - pRight + pLeft;
depth_table_ptr = gDepth_shade_table->pixels;
@ -316,6 +341,12 @@ void DoPSPowerHeadup(int pY, int pLevel, char* pName, int pBar_colour) {
int i;
LOG_TRACE("(%d, %d, \"%s\", %d)", pY, pLevel, pName, pBar_colour);
#ifdef DETHRACE_3DFX_PATCH
if (gBack_screen->type == BR_PMT_RGB_565) {
pBar_colour = PaletteEntry16Bit(gRender_palette, pBar_colour);
}
#endif
DimRectangle(gBack_screen, gCurrent_graf_data->ps_dim_left, pY, gCurrent_graf_data->ps_dim_right, gCurrent_graf_data->ps_dim_height + pY, 1);
TransDRPixelmapText(gBack_screen, gCurrent_graf_data->ps_name_left, gCurrent_graf_data->ps_name_top_border + pY, gFonts + 6, pName, gBack_screen->width);
@ -1158,7 +1189,11 @@ void DoInstruments(tU32 pThe_time) {
+ (double)the_wobble_y),
gProgram_state.current_car.tacho_needle_colour[gProgram_state.cockpit_on]);
} else if (tacho_image != NULL) {
#ifdef DETHRACE_3DFX_PATCH
DRPixelmapRectangleCopy(
#else
BrPixelmapRectangleCopy(
#endif
gBack_screen,
the_wobble_x + gProgram_state.current_car.tacho_x[gProgram_state.cockpit_on],
the_wobble_y + gProgram_state.current_car.tacho_y[gProgram_state.cockpit_on],

View File

@ -131,6 +131,18 @@ char* gError_messages[126] = {
"Net contents too big %",
"File % is corrupted",
"Random number out of range (%)",
#ifdef DETHRACE_3DFX_PATCH
"Couldn't lock pixelmap %",
"% should be locked but isn't",
"Cannot purify pixelmap %",
"File % must start with \"%\"",
"Can't cope with version % for %",
"Cannot tile pixelmap %",
"Mysterious \"%\" in %",
"Can only dim rectangles of gBack_screen",
"Invalid material alpha"
#endif
};
int gError_code;
char* gPalette_copy__errors; // suffix added to avoid duplicate symbol

View File

@ -1838,7 +1838,16 @@ void InitialiseFlicPanel(int pIndex, int pLeft, int pTop, int pWidth, int pHeigh
"Bruce bug at line %d, file ..\\..\\source\\common\\flicplay.c",
68);
}
gPanel_buffer[pIndex] = DRPixelmapAllocate(gScreen->type, pWidth, pHeight, the_pixels, 0);
gPanel_buffer[pIndex] = DRPixelmapAllocate(
#ifdef DETHRACE_3DFX_PATCH
gBack_screen->type,
#else
gScreen->type,
#endif
pWidth,
pHeight,
the_pixels,
0);
}
// IDA: void __usercall DisposeFlicPanel(int pIndex@<EAX>)

View File

@ -34,3 +34,6 @@ int gMax_texture_side;
int gDevious_2d;
int gMax_texture_aspect_ratio;
int gMaterial_fogging;
// Added
int gVoodoo_rush_mode;

View File

@ -37,4 +37,6 @@ extern int gDevious_2d;
extern int gMax_texture_aspect_ratio;
extern int gMaterial_fogging;
extern int gVoodoo_rush_mode;
#endif

View File

@ -10,6 +10,7 @@
#include "finteray.h"
#include "flicplay.h"
#include "globvars.h"
#include "globvrbm.h"
#include "globvrpb.h"
#include "grafdata.h"
#include "harness/hooks.h"
@ -336,6 +337,11 @@ void DRDrawLine(br_pixelmap* pDestn, int pX1, int pY1, int pX2, int pY2, int pCo
int the_diff;
LOG_TRACE("(%p, %d, %d, %d, %d, %d)", pDestn, pX1, pY1, pX2, pY2, pColour);
#ifdef DETHRACE_3DFX_PATCH
if (gBack_screen->type == BR_PMT_RGB_565) {
pColour = PaletteEntry16Bit(gRender_palette, pColour);
}
#endif
BrPixelmapLine(pDestn, pX1, pY1, pX2, pY2, pColour);
}
@ -425,7 +431,10 @@ void CopyWords(char* pDst, char* pSrc, int pN) {
tU16* dst;
tU16* src;
LOG_TRACE("(\"%s\", \"%s\", %d)", pDst, pSrc, pN);
NOT_IMPLEMENTED();
dst = (tU16*)pDst;
src = (tU16*)pSrc;
BrMemCpy(dst, src, pN);
}
// IDA: void __usercall Copy8BitStripImageTo16Bit(br_pixelmap *pDest@<EAX>, br_int_16 pDest_x@<EDX>, br_int_16 pOffset_x@<EBX>, br_int_16 pDest_y@<ECX>, br_int_16 pOffset_y, tS8 *pSource, br_int_16 pSource_x, br_int_16 pSource_y, br_uint_16 pWidth, br_uint_16 pHeight)
@ -442,7 +451,66 @@ void Copy8BitStripImageTo16Bit(br_pixelmap* pDest, br_int_16 pDest_x, br_int_16
char* destn_ptr;
char* destn_ptr2;
LOG_TRACE("(%p, %d, %d, %d, %d, %p, %d, %d, %d, %d)", pDest, pDest_x, pOffset_x, pDest_y, pOffset_y, pSource, pSource_x, pSource_y, pWidth, pHeight);
NOT_IMPLEMENTED();
height = *(uint16_t*)pSource;
pSource = pSource + 2;
if (pDest_y + pOffset_y >= 0) {
destn_ptr = (char*)pDest->pixels + pDest->row_bytes * (pDest_y + pOffset_y);
} else {
pSource = SkipLines(pSource, -pDest_y - pOffset_y);
destn_ptr = (char*)pDest->pixels;
height += pDest_y + pOffset_y;
pOffset_y = 0;
pDest_y = 0;
}
if (height + pDest_y + pOffset_y > pDest->height) {
height = pDest->height - pDest_y - pOffset_y;
}
if (gBack_screen->type == BR_PMT_RGB_565) {
pDest_x *= 2;
pOffset_x *= 2;
if (pDest_x + pOffset_x > 0) {
destn_ptr += 2 * pDest_x + 2 * pOffset_x;
}
destn_width = 2 * pDest->width;
}
for (i = 0; i < height; i++) {
number_of_chunks = *pSource;
pSource++;
destn_ptr2 = destn_ptr;
x_byte = pOffset_x + pDest_x;
for (j = 0; j < number_of_chunks; j++) {
chunk_length = *pSource;
pSource++;
if (chunk_length >= 0) {
old_x_byte = x_byte;
x_byte += chunk_length;
if (old_x_byte >= 0) {
destn_ptr2 += chunk_length;
} else if (x_byte > 0) {
destn_ptr2 += chunk_length + old_x_byte;
}
} else {
old_x_byte = x_byte;
x_byte += -chunk_length;
if (old_x_byte >= 0) {
if (destn_width >= x_byte) {
CopyWords(destn_ptr2, (char*)pSource, -chunk_length);
destn_ptr2 += -chunk_length;
} else if (old_x_byte < destn_width) {
CopyWords(destn_ptr2, (char*)pSource, destn_width - old_x_byte);
}
} else if (x_byte > 0) {
CopyWords(destn_ptr2, (char*)&pSource[-old_x_byte], -chunk_length + old_x_byte);
destn_ptr2 += -chunk_length + old_x_byte;
}
pSource += -chunk_length;
}
}
destn_ptr += pDest->row_bytes;
}
}
// IDA: void __usercall CopyStripImage(br_pixelmap *pDest@<EAX>, br_int_16 pDest_x@<EDX>, br_int_16 pOffset_x@<EBX>, br_int_16 pDest_y@<ECX>, br_int_16 pOffset_y, tS8 *pSource, br_int_16 pSource_x, br_int_16 pSource_y, br_uint_16 pWidth, br_uint_16 pHeight)
@ -460,6 +528,21 @@ void CopyStripImage(br_pixelmap* pDest, br_int_16 pDest_x, br_int_16 pOffset_x,
char* destn_ptr2;
LOG_TRACE8("(%p, %d, %d, %d, %d, %p, %d, %d, %d, %d)", pDest, pDest_x, pOffset_x, pDest_y, pOffset_y, pSource, pSource_x, pSource_y, pWidth, pHeight);
if (gBack_screen->type == BR_PMT_RGB_565) {
Copy8BitStripImageTo16Bit(
pDest,
pDest_x,
pOffset_x,
pDest_y,
pOffset_y,
pSource,
pSource_x,
pSource_y,
pWidth,
pHeight);
return;
}
height = *(uint16_t*)pSource;
pSource = pSource + 2;
if (pDest_y + pOffset_y >= 0) {
@ -549,6 +632,7 @@ void SetBRenderScreenAndBuffers(int pX_offset, int pY_offset, int pWidth, int pH
if (gDepth_buffer == NULL) {
FatalError(kFatalError_AllocateZBuffer);
}
BrZbBegin(gRender_screen->type, gDepth_buffer->type);
gBrZb_initialized = 1;
}
@ -563,7 +647,7 @@ void SetIntegerMapRenders(void) {
gMap_render_height_i = ((int)gMap_render_height) & ~1;
if (gReal_graf_data_index != 0) {
gMap_render_x_i = 2 * gMap_render_x_i;
gMap_render_y_i = 2 * gMap_render_y_i + 40;
gMap_render_y_i = 2 * gMap_render_y_i + HIRES_Y_OFFSET;
gMap_render_width_i = 2 * gMap_render_width_i;
gMap_render_height_i = 2 * gMap_render_height_i;
}
@ -641,6 +725,9 @@ void DRSetPaletteEntries(br_pixelmap* pPalette, int pFirst_colour, int pCount) {
((br_int_32*)pPalette->pixels)[0] = 0;
}
memcpy(gCurrent_palette_pixels + 4 * pFirst_colour, (char*)pPalette->pixels + 4 * pFirst_colour, 4 * pCount);
#ifdef DETHRACE_3DFX_PATCH
g16bit_palette_valid = 0;
#endif
if (!gFaded_palette) {
PDSetPaletteEntries(pPalette, pFirst_colour, pCount);
}
@ -653,6 +740,9 @@ void DRSetPalette3(br_pixelmap* pThe_palette, int pSet_current_palette) {
if (pSet_current_palette) {
memcpy(gCurrent_palette_pixels, pThe_palette->pixels, 0x400u);
#ifdef DETHRACE_3DFX_PATCH
g16bit_palette_valid = 0;
#endif
}
if (!gFaded_palette) {
PDSetPalette(pThe_palette);
@ -667,6 +757,9 @@ void DRSetPalette2(br_pixelmap* pThe_palette, int pSet_current_palette) {
((br_int_32*)pThe_palette->pixels)[0] = 0;
if (pSet_current_palette) {
memcpy(gCurrent_palette_pixels, pThe_palette->pixels, 0x400u);
#ifdef DETHRACE_3DFX_PATCH
g16bit_palette_valid = 0;
#endif
}
if (!gFaded_palette) {
PDSetPalette(pThe_palette);
@ -685,11 +778,18 @@ void DRSetPalette(br_pixelmap* pThe_palette) {
void InitializePalettes(void) {
int j;
gCurrent_palette_pixels = BrMemAllocate(0x400u, kMem_cur_pal_pixels);
#ifdef DETHRACE_3DFX_PATCH
g16bit_palette_valid = 0;
#endif
gCurrent_palette = DRPixelmapAllocate(BR_PMT_RGBX_888, 1u, 256, gCurrent_palette_pixels, 0);
gRender_palette = BrTableFind("DRRENDER.PAL");
if (gRender_palette == NULL) {
FatalError(kFatalError_RequiredPalette);
}
#ifdef DETHRACE_3DFX_PATCH
NobbleNonzeroBlacks(gRender_palette);
#endif
gOrig_render_palette = BrPixelmapAllocateSub(gRender_palette, 0, 0, gRender_palette->width, gRender_palette->height);
gOrig_render_palette->pixels = BrMemAllocate(0x400u, kMem_render_pal_pixels);
memcpy(gOrig_render_palette->pixels, gRender_palette->pixels, 0x400u);
@ -954,7 +1054,7 @@ void DrawMapBlip(tCar_spec* pCar, tU32 pTime, br_matrix34* pTrans, br_vector3* p
break;
case 1:
map_pos.v[0] = map_pos.v[0] * 2.f;
map_pos.v[1] = map_pos.v[1] * 2.f + 40.f;
map_pos.v[1] = map_pos.v[1] * 2.f + HIRES_Y_OFFSET;
break;
default:
TELL_ME_IF_WE_PASS_THIS_WAY();
@ -962,6 +1062,13 @@ void DrawMapBlip(tCar_spec* pCar, tU32 pTime, br_matrix34* pTrans, br_vector3* p
period = 256; // Must be power of 2
colours[0] = pColour;
colours[1] = OppositeColour(pColour);
#ifdef DETHRACE_3DFX_PATCH
if (gBack_screen->type != BR_PMT_INDEX_8) {
colours[0] = PaletteEntry16Bit(gRender_palette, colours[0]);
colours[1] = PaletteEntry16Bit(gRender_palette, colours[1]);
}
#endif
BrMatrix34Mul(&car_in_map_space, pTrans, &gCurrent_race.map_transformation);
bearing = FastScalarArcTan2(car_in_map_space.m[2][0], car_in_map_space.m[2][1]);
@ -1029,10 +1136,20 @@ void DrawMapSmallBlip(tU32 pTime, br_vector3* pPos, int pColour) {
BrMatrix34ApplyP(&map_pos, pPos, &gCurrent_race.map_transformation);
if (gReal_graf_data_index != 0) {
map_pos.v[0] = 2.f * map_pos.v[0];
map_pos.v[1] = 2.f * map_pos.v[1] + 40.f;
map_pos.v[1] = 2.f * map_pos.v[1] + HIRES_Y_OFFSET;
}
#ifdef DETHRACE_3DFX_PATCH
if (gBack_screen->type == BR_PMT_RGB_565) {
offset = ((int)map_pos.v[0] * 2) + gBack_screen->row_bytes * (int)map_pos.v[1];
pColour = PaletteEntry16Bit(gRender_palette, pColour);
br_uint_8* p1 = &(((br_uint_8*)gBack_screen->pixels)[offset]);
*((br_uint_16*)(p1)) = pColour;
} else
#endif
{
offset = (int)map_pos.v[0] + gBack_screen->row_bytes * (int)map_pos.v[1];
((br_uint_8*)gBack_screen->pixels)[offset] = pColour;
}
offset = (int)map_pos.v[0] + gBack_screen->row_bytes * (int)map_pos.v[1];
((br_uint_8*)gBack_screen->pixels)[offset] = pColour;
}
}
@ -1081,7 +1198,7 @@ void TryThisEdge(tCar_spec* pCar, br_vector3* pLight, int pIndex_1, br_scalar pS
dot_2 = pSign_2 * pLight->v[pIndex_2];
mult = dot_1 * dot_2;
if (mult < 0 || (mult == 0 && (dot_1 > 0 || dot_2 > 0))) {
if (gShadow_clip_plane_count < 6) {
if (gShadow_clip_plane_count < BR_MAX_CLIP_PLANES) {
MungeClipPlane(pLight, pCar, &gShadow_points[pPoint_index_1], &gShadow_points[pPoint_index_2], pY_offset);
}
}
@ -1099,14 +1216,20 @@ br_scalar DistanceFromPlane(br_vector3* pPos, br_scalar pA, br_scalar pB, br_sca
void DisableLights(void) {
int i;
LOG_TRACE("()");
NOT_IMPLEMENTED();
for (i = 0; i < gNumber_of_lights; i++) {
BrLightDisable(gLight_array[i]);
}
}
// IDA: void __cdecl EnableLights()
void EnableLights(void) {
int i;
LOG_TRACE("()");
NOT_IMPLEMENTED();
for (i = 0; i < gNumber_of_lights; i++) {
BrLightEnable(gLight_array[i]);
}
}
// IDA: void __usercall ProcessShadow(tCar_spec *pCar@<EAX>, br_actor *pWorld@<EDX>, tTrack_spec *pTrack_spec@<EBX>, br_actor *pCamera@<ECX>, br_matrix34 *pCamera_to_world_transform, br_scalar pDistance_factor)
@ -1214,7 +1337,7 @@ void ProcessShadow(tCar_spec* pCar, br_actor* pWorld, tTrack_spec* pTrack_spec,
TryThisEdge(pCar, &light_ray_car, 0, -1.0, 2, 1.0, 3, 7, y_offset);
TryThisEdge(pCar, &light_ray_car, 0, -1.0, 2, -1.0, 2, 6, y_offset);
TryThisEdge(pCar, &light_ray_car, 0, 1.0, 2, -1.0, 5, 1, y_offset);
for (i = 0; i < gShadow_clip_plane_count; ++i) {
for (i = 0; i < gShadow_clip_plane_count; i++) {
BrClipPlaneEnable(gShadow_clip_planes[i].clip);
}
face_count = GetPrecalculatedFacesUnderCar(pCar, &face_ref);
@ -1307,9 +1430,17 @@ void ProcessShadow(tCar_spec* pCar, br_actor* pWorld, tTrack_spec* pTrack_spec,
if (list_ptr->v[0].v[1] >= first_poly_below || list_ptr->v[1].v[1] >= first_poly_below || list_ptr->v[2].v[1] >= first_poly_below) {
if (gFancy_shadow) {
faces[f_num].material = list_ptr->material;
if (list_ptr->material && list_ptr->material->colour_map && (list_ptr->material->flags & BR_MATF_LIGHT) == 0) {
list_ptr->material->flags |= BR_MATF_SMOOTH | BR_MATF_LIGHT;
BrMaterialUpdate(list_ptr->material, BR_MATU_RENDERING);
#ifdef DETHRACE_3DFX_PATCH
if (gShade_tables_do_not_work) {
list_ptr->material->ka = 0.75f;
BrMaterialUpdate(list_ptr->material, BR_MATU_LIGHTING);
} else
#endif
{
if (list_ptr->material && list_ptr->material->colour_map && (list_ptr->material->flags & BR_MATF_LIGHT) == 0) {
list_ptr->material->flags |= BR_MATF_SMOOTH | BR_MATF_LIGHT;
BrMaterialUpdate(list_ptr->material, BR_MATU_RENDERING);
}
}
} else {
faces[f_num].material = gShadow_material;
@ -1377,7 +1508,13 @@ void ProcessShadow(tCar_spec* pCar, br_actor* pWorld, tTrack_spec* pTrack_spec,
camera_ptr->hither_z += camera_hither_fudge;
}
if (f_num) {
#ifdef DETHRACE_3DFX_PATCH
DisableLights();
#endif
BrZbSceneRenderBegin(gUniverse_actor, gCamera, gRender_screen, gDepth_buffer);
#ifdef DETHRACE_3DFX_PATCH
EnableLights();
#endif
gShadow_model->vertices = verts;
gShadow_model->faces = faces;
gShadow_model->nfaces = f_num;
@ -1405,6 +1542,13 @@ void ProcessShadow(tCar_spec* pCar, br_actor* pWorld, tTrack_spec* pTrack_spec,
if (gFancy_shadow) {
material = gShadow_model->faces[i].material;
if (material) {
#ifdef DETHRACE_3DFX_PATCH
if (gShade_tables_do_not_work) {
material->ka = 1.0f;
BrMaterialUpdate(material, BR_MATU_LIGHTING);
continue;
}
#endif
if (material->colour_map && (material->flags & BR_MATF_LIGHT) != 0) {
material->flags &= ~(BR_MATF_LIGHT | BR_MATF_PRELIT | BR_MATF_SMOOTH);
BrMaterialUpdate(material, BR_MATU_RENDERING);
@ -1495,9 +1639,9 @@ void FlashyMapCheckpoint(int pIndex, tU32 pTime) {
case 1:
DimRectangle(gBack_screen,
2 * gCurrent_race.checkpoints[pIndex].map_left[0],
2 * gCurrent_race.checkpoints[pIndex].map_top[0] + 40,
2 * gCurrent_race.checkpoints[pIndex].map_top[0] + HIRES_Y_OFFSET,
2 * gCurrent_race.checkpoints[pIndex].map_right[0],
2 * gCurrent_race.checkpoints[pIndex].map_bottom[0] + 40,
2 * gCurrent_race.checkpoints[pIndex].map_bottom[0] + HIRES_Y_OFFSET,
0);
break;
default:
@ -1527,7 +1671,18 @@ int ConditionallyFillWithSky(br_pixelmap* pPixelmap) {
} else {
bgnd_col = 0;
}
#ifdef DETHRACE_3DFX_PATCH
if (pPixelmap->type == BR_PMT_RGB_565) {
bgnd_col = PaletteEntry16Bit(gRender_palette, bgnd_col);
bgnd_col = (bgnd_col << 16) | bgnd_col;
}
#endif
BrPixelmapFill(pPixelmap, bgnd_col);
#ifdef DETHRACE_3DFX_PATCH
// Added by dethrace to ensure the pixel writes are flushed before 3d geometry
BrPixelmapFlush(pPixelmap);
#endif
return 1;
}
@ -1557,7 +1712,12 @@ void RenderAFrame(int pDepth_mask_on) {
tCar_spec* car;
LOG_TRACE("(%d)", pDepth_mask_on);
gRender_screen->pixels = gBack_screen->pixels;
#ifdef DETHRACE_3DFX_PATCH
if (gVoodoo_rush_mode >= 1) {
gRender_screen->pixels = gBack_screen->pixels;
}
#endif
the_time = GetTotalTime();
old_pixels = gRender_screen->pixels;
cockpit_on = gProgram_state.cockpit_on && gProgram_state.cockpit_image_index >= 0 && !gMap_mode;
@ -1575,6 +1735,7 @@ void RenderAFrame(int pDepth_mask_on) {
if (gReal_graf_data_index) {
BrPixelmapRectangleFill(gBack_screen, 0, 0, 640, 40, 0);
BrPixelmapRectangleFill(gBack_screen, 0, 440, 640, 40, 0);
DRPixelmapDoubledCopy(
gBack_screen,
gCurrent_race.map_image,
@ -1586,6 +1747,14 @@ void RenderAFrame(int pDepth_mask_on) {
DRPixelmapCopy(gBack_screen, gCurrent_race.map_image);
}
}
#ifdef DETHRACE_3DFX_PATCH
// Added by dethrace
// 3d scene is drawn on top of the 2d map, so we must ensure that all the 2d pixel
// writes have been flushed to the framebuffer first
BrPixelmapFlush(gBack_screen);
#endif
DimRectangle(
gBack_screen,
gMap_render_x_i - gCurrent_graf_data->map_render_x_marg,
@ -1662,14 +1831,31 @@ void RenderAFrame(int pDepth_mask_on) {
}
gRendering_mirror = 0;
DoSpecialCameraEffect(gCamera, &gCamera_to_world);
#ifdef DETHRACE_3DFX_PATCH
if (!ConditionallyFillWithSky(gRender_screen->width == gBack_screen->width ? gBack_screen : gRender_screen)
#else
if (!ConditionallyFillWithSky(gRender_screen)
#endif
&& !gProgram_state.cockpit_on
&& !(gAction_replay_camera_mode && gAction_replay_mode)) {
ExternalSky(gRender_screen, gDepth_buffer, gCamera, &gCamera_to_world);
#ifdef DETHRACE_3DFX_PATCH
if (!gBlitting_is_slow)
#endif
{
ExternalSky(gRender_screen, gDepth_buffer, gCamera, &gCamera_to_world);
}
}
#ifdef DETHRACE_3DFX_PATCH
PDUnlockRealBackScreen(1);
#endif
#if !defined(DETHRACE_FIX_BUGS)
// in map mode, the scene is rendered 3 times. We have no idea why.
for (i = 0; i < (gMap_mode ? 3 : 1); i++)
#elif defined(DETHRACE_3DFX_PATCH)
for (i = 0; i < (gMap_mode && !gSmall_frames_are_slow ? 3 : 1); i++)
#endif
{
RenderShadows(gUniverse_actor, &gProgram_state.track_spec, gCamera, &gCamera_to_world);
@ -1689,12 +1875,50 @@ void RenderAFrame(int pDepth_mask_on) {
RenderProximityRays(gRender_screen, gDepth_buffer, gCamera, &gCamera_to_world, gFrame_period);
BrZbSceneRenderEnd();
}
#ifdef DETHRACE_3DFX_PATCH
PDLockRealBackScreen(1);
#endif
BrMatrix34Copy(&gCamera->t.t.mat, &old_camera_matrix);
#ifdef DETHRACE_3DFX_PATCH
if (cockpit_on) {
PDUnlockRealBackScreen(1);
PDLockRealBackScreen(1);
CopyStripImage(
gBack_screen,
-gCurrent_graf_data->cock_margin_x,
gScreen_wobble_x,
-gCurrent_graf_data->cock_margin_y,
gScreen_wobble_y,
gProgram_state.current_car.cockpit_images[gProgram_state.cockpit_image_index],
0,
0,
gCurrent_graf_data->total_cock_width,
gCurrent_graf_data->total_cock_height);
}
#endif
if (gMirror_on__graphics) {
#ifdef DETHRACE_3DFX_PATCH
if (gVoodoo_rush_mode >= 1) {
gRearview_screen->pixels = gBack_screen->pixels;
}
gRearview_screen->base_x = gScreen_wobble_x + gProgram_state.current_car.mirror_left;
gRearview_screen->base_y = gScreen_wobble_y + gProgram_state.current_car.mirror_top;
#endif
BrPixelmapFill(gRearview_depth_buffer, 0xFFFFFFFF);
gRendering_mirror = 1;
DoSpecialCameraEffect(gRearview_camera, &gRearview_camera_to_world);
ConditionallyFillWithSky(gRearview_screen);
#ifdef DETHRACE_3DFX_PATCH
PDUnlockRealBackScreen(1);
// Added by dethrace
// Rearview mirror is drawn on top of the 2d cockpit, so we must ensure that all the 2d pixel
// writes have been flushed to the framebuffer first
BrPixelmapFlush(gBack_screen);
// ---
#endif
BrZbSceneRenderBegin(gUniverse_actor, gRearview_camera, gRearview_screen, gRearview_depth_buffer);
ProcessNonTrackActors(
gRearview_screen,
@ -1710,7 +1934,14 @@ void RenderAFrame(int pDepth_mask_on) {
ProcessTrack(gUniverse_actor, &gProgram_state.track_spec, gRearview_camera, &gRearview_camera_to_world, 1);
}
RenderSplashes();
#ifdef DETHRACE_3DFX_PATCH
RenderSmoke(gRearview_screen, gRearview_depth_buffer, gRearview_camera, &gRearview_camera_to_world, gFrame_period);
RenderSparks(gRearview_screen, gRearview_depth_buffer, gRearview_camera, &gRearview_camera_to_world, gFrame_period);
#endif
BrZbSceneRenderEnd();
#ifdef DETHRACE_3DFX_PATCH
PDLockRealBackScreen(1);
#endif
BrMatrix34Copy(&gRearview_camera->t.t.mat, &old_mirror_cam_matrix);
gRendering_mirror = 0;
}
@ -1807,6 +2038,7 @@ void RenderAFrame(int pDepth_mask_on) {
gBack_screen->base_x = real_base_x;
gBack_screen->base_y = real_base_y;
} else {
#if !defined(DETHRACE_3DFX_PATCH)
if (cockpit_on) {
CopyStripImage(
gBack_screen,
@ -1831,9 +2063,14 @@ void RenderAFrame(int pDepth_mask_on) {
gProgram_state.current_car.mirror_bottom - gProgram_state.current_car.mirror_top);
}
}
#endif
DimAFewBits();
DoDamageScreen(the_time);
if (!gAction_replay_mode || gAR_fudge_headups) {
// Added by dethrace
// Pratcam is drawn on top of the 2d cockpit, so we must ensure that all the 2d pixel
// writes have been flushed to the framebuffer first
BrPixelmapFlush(gBack_screen);
DoPratcam(the_time);
DoHeadups(the_time);
}
@ -2090,6 +2327,13 @@ void DRPixelmapRectangleMaskedCopy(br_pixelmap* pDest, br_int_16 pDest_x, br_int
tU8* conv_table;
LOG_TRACE("(%p, %d, %d, %p, %d, %d, %d, %d)", pDest, pDest_x, pDest_y, pSource, pSource_x, pSource_y, pWidth, pHeight);
#ifdef DETHRACE_3DFX_PATCH
if (pDest->type == BR_PMT_RGB_565 && pSource->type == BR_PMT_INDEX_8) {
Copy8BitTo16BitRectangleWithTransparency(pDest, pDest_x, pDest_y, pSource, pSource_x, pSource_y, pWidth, pHeight,
gCurrent_conversion_table == NULL ? gCurrent_palette : gFlic_palette);
return;
}
#endif
source_ptr = (tU8*)pSource->pixels + (pSource->row_bytes * pSource_y + pSource_x);
dest_ptr = (tU8*)pDest->pixels + (pDest->row_bytes * pDest_y + pDest_x);
source_row_wrap = pSource->row_bytes - pWidth;
@ -2129,7 +2373,7 @@ void DRPixelmapRectangleMaskedCopy(br_pixelmap* pDest, br_int_16 pDest_x, br_int
dest_row_wrap += pDest_x + pWidth - pDest->width;
pWidth = pDest->width - pDest_x;
}
// LOG_DEBUG("2 (src->width: %d, src->height: %d, pDest_x: %d, pDest_y: %d, pSource_x: %d, pSource_y: %d, pWidth: %d, pHeight: %d)", pSource->width, pSource->height, pDest_x, pDest_y, pSource_x, pSource_y, pWidth, pHeight);
if (gCurrent_conversion_table != NULL) {
conv_table = gCurrent_conversion_table->pixels;
for (y_count = 0; y_count < pHeight; y_count++) {
@ -2185,7 +2429,14 @@ void DRPixelmapRectangleOnscreenCopy(br_pixelmap* pDest, br_int_16 pDest_x, br_i
tU8* source_ptr;
tU8* dest_ptr;
tU8* conv_table;
// LOG_TRACE("(%p, %d, %d, %p, %d, %d, %d, %d)", pDest, pDest_x, pDest_y, pSource, pSource_x, pSource_y, pWidth, pHeight);
LOG_TRACE("(%p, %d, %d, %p, %d, %d, %d, %d)", pDest, pDest_x, pDest_y, pSource, pSource_x, pSource_y, pWidth, pHeight);
#ifdef DETHRACE_3DFX_PATCH
if (pDest->type == BR_PMT_RGB_565 && pSource->type == BR_PMT_INDEX_8) {
Copy8BitToOnscreen16BitRectangleWithTransparency(pDest, pDest_x, pDest_y, pSource, pSource_x, pSource_y, pWidth, pHeight, gCurrent_palette);
return;
}
#endif
source_row_wrap = pSource->row_bytes - pWidth;
dest_row_wrap = pDest->row_bytes - pWidth;
@ -2224,6 +2475,12 @@ void DRPixelmapRectangleShearedCopy(br_pixelmap* pDest, br_int_16 pDest_x, br_in
tX1616 current_shear;
LOG_TRACE("(%p, %d, %d, %p, %d, %d, %d, %d, %d)", pDest, pDest_x, pDest_y, pSource, pSource_x, pSource_y, pWidth, pHeight, pShear);
#ifdef DETHRACE_3DFX_PATCH
if (pDest->type == BR_PMT_RGB_565 && pSource->type == BR_PMT_INDEX_8) {
Copy8BitRectangleTo16BitRhombusWithTransparency(pDest, pDest_x, pDest_y, pSource, pSource_x, pSource_y, pWidth, pHeight, pShear, gCurrent_palette);
return;
}
#endif
current_shear = 0;
last_shear_x = 0;
source_ptr = (tU8*)pSource->pixels + pSource_x + pSource_y * pSource->row_bytes;
@ -2366,7 +2623,11 @@ int AllocateTransientBitmap(int pWidth, int pHeight, int pUser_data) {
for (bm_index = 0; bm_index < COUNT_OF(gTransient_bitmaps); bm_index++) {
if (gTransient_bitmaps[bm_index].pixmap == NULL) {
#ifdef DETHRACE_3DFX_PATCH
gTransient_bitmaps[bm_index].pixmap = DRPixelmapAllocate(gBack_screen->type, pWidth + 8, pHeight, NULL, 0);
#else
gTransient_bitmaps[bm_index].pixmap = DRPixelmapAllocate(BR_PMT_INDEX_8, pWidth + 8, pHeight, NULL, 0);
#endif
gTransient_bitmaps[bm_index].in_use = 0;
gTransient_bitmaps[bm_index].user_data = pUser_data;
return bm_index;
@ -3031,7 +3292,7 @@ void InitShadow(void) {
br_vector3 temp_v;
LOG_TRACE("()");
for (i = 0; i < 8; i++) {
for (i = 0; i < COUNT_OF(gShadow_clip_planes); i++) {
gShadow_clip_planes[i].clip = BrActorAllocate(BR_ACTOR_CLIP_PLANE, NULL);
BrActorAdd(gUniverse_actor, gShadow_clip_planes[i].clip);
BrClipPlaneDisable(gShadow_clip_planes[i].clip);
@ -3137,6 +3398,12 @@ void DRPixelmapDoubledCopy(br_pixelmap* pDestn, br_pixelmap* pSource, int pSourc
int width_over_2;
LOG_TRACE("(%p, %p, %d, %d, %d, %d)", pDestn, pSource, pSource_width, pSource_height, pX_offset, pY_offset);
#ifdef DETHRACE_3DFX_PATCH
if (pDestn->type != pSource->type && pDestn->type == BR_PMT_RGB_565 && pSource->type == BR_PMT_INDEX_8) {
CopyDoubled8BitTo16BitRectangle(pDestn, pSource, pSource_width, pSource_height, pX_offset, pY_offset, gCurrent_palette);
return;
}
#endif
dst_row_skip = 2 * pDestn->row_bytes - 2 * pSource_width;
src_row_skip = (pSource->row_bytes - pSource_width) / 2;
sptr = (tU16*)((tU8*)pSource->pixels - 2 * src_row_skip + 2 * (pSource->row_bytes * pSource_height / 2));

View File

@ -12,6 +12,7 @@
#include "errors.h"
#include "flicplay.h"
#include "globvars.h"
#include "globvrbm.h"
#include "globvrkm.h"
#include "globvrpb.h"
#include "grafdata.h"
@ -166,6 +167,23 @@ void AllocateRearviewPixelmap(void) {
char* rear_screen_pixels;
LOG_TRACE("()");
#ifdef DETHRACE_3DFX_PATCH
if (gRearview_screen != NULL) {
BrPixelmapFree(gRearview_screen);
gRearview_screen = NULL;
}
if (gProgram_state.mirror_on) {
gRearview_screen = BrPixelmapAllocateSub(
gBack_screen,
gProgram_state.current_car.mirror_left,
gProgram_state.current_car.mirror_top,
gProgram_state.current_car.mirror_right - gProgram_state.current_car.mirror_left,
gProgram_state.current_car.mirror_bottom - gProgram_state.current_car.mirror_top);
gRearview_depth_buffer = gDepth_buffer;
gRearview_screen->origin_x = gRearview_screen->width / 2;
gRearview_screen->origin_y = gRearview_screen->height / 2;
}
#else
if (gRearview_screen) {
BrMemFree(gRearview_screen->pixels);
BrPixelmapFree(gRearview_screen);
@ -192,6 +210,7 @@ void AllocateRearviewPixelmap(void) {
gRearview_screen->origin_y = gRearview_screen->height / 2;
gRearview_depth_buffer = BrPixelmapMatch(gRearview_screen, BR_PMMATCH_DEPTH_16);
}
#endif
}
// IDA: void __cdecl ReinitialiseRearviewCamera()
@ -221,6 +240,15 @@ void ReinitialiseRenderStuff(void) {
gProgram_state.current_render_right = gProgram_state.current_car.render_right[gProgram_state.cockpit_image_index];
gProgram_state.current_render_bottom = gProgram_state.current_car.render_bottom[gProgram_state.cockpit_image_index];
} else {
#ifdef DETHRACE_3DFX_PATCH
if (gSmall_frames_are_slow) {
gProgram_state.current_render_top = 0;
gProgram_state.current_render_right = gGraf_specs[gGraf_spec_index].total_width;
gProgram_state.current_render_left = 0;
gProgram_state.current_render_bottom = gGraf_specs[gGraf_spec_index].total_height;
return;
}
#endif
gProgram_state.current_render_top = (gGraf_specs[gGraf_spec_index].total_height / 18 & ~1) * gRender_indent;
gProgram_state.current_render_left = (gGraf_specs[gGraf_spec_index].total_width / 18 & ~3) * gRender_indent;
x_diff = gGraf_specs[gGraf_spec_index].total_width - gProgram_state.current_render_left;
@ -320,26 +348,187 @@ void AustereWarning(void) {
// IDA: void __cdecl InitLineStuff()
void InitLineStuff(void) {
LOG_TRACE("()");
NOT_IMPLEMENTED();
// HACK: originally 2 vertices
gLine_model = BrModelAllocate("gLine_model", 3 /*2*/, 1);
gLine_material = BrMaterialAllocate("gLine_material");
gLine_actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
if (!gLine_model || !gLine_material || !gLine_actor) {
FatalError(94);
}
gLine_actor->identifier = "gLine_actor";
gLine_actor->render_style = BR_RSTYLE_EDGES;
gLine_actor->model = gLine_model;
gLine_actor->material = gLine_material;
gLine_model->flags = BR_MODF_QUICK_UPDATE | BR_MODF_KEEP_ORIGINAL;
gLine_model->faces->vertices[0] = 0;
gLine_model->faces->vertices[1] = 0;
gLine_model->faces->vertices[2] = 1;
// HACK: override the 2 vertices + EDGES with 3 vertices + FACES
gLine_model->faces->vertices[1] = 2;
gLine_actor->render_style = BR_RSTYLE_FACES;
// HACK end
gLine_material->flags = BR_MATF_TWO_SIDED | BR_MATF_SMOOTH | BR_MATF_PRELIT | BR_MATF_LIGHT;
gLine_model->faces[0].flags = BR_FACEF_COPLANAR_0 | BR_FACEF_COPLANAR_2;
BrModelAdd(gLine_model);
BrMaterialAdd(gLine_material);
BrActorAdd(gDont_render_actor, gLine_actor);
}
// IDA: void __cdecl InitSmokeStuff()
void InitSmokeStuff(void) {
static br_token_value fadealpha[3];
static br_token_value fadealpha[3] = { { BRT_BLEND_B, { .u32 = 1 } }, { BRT_OPACITY_X, { .x = 0x4B0000 } }, { 0 } };
tPath_name path;
LOG_TRACE("()");
NOT_IMPLEMENTED();
gBlend_model = BrModelAllocate("gBlend_model", 4, 2);
gBlend_material = BrMaterialAllocate("gBlend_material");
gBlend_actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
if (!gBlend_model || !gBlend_material || !gBlend_actor) {
FatalError(94);
}
gBlend_actor->identifier = "gBlend_actor";
gBlend_actor->model = gBlend_model;
gBlend_actor->material = gBlend_material;
gBlend_model->faces[0].vertices[0] = 0;
gBlend_model->faces[0].vertices[1] = 1;
gBlend_model->faces[0].vertices[2] = 2;
gBlend_model->faces[1].vertices[0] = 2;
gBlend_model->faces[1].vertices[1] = 3;
gBlend_model->faces[1].vertices[2] = 0;
gBlend_model->vertices[0].p.v[0] = -1.0f;
gBlend_model->vertices[0].p.v[1] = 1.0f;
gBlend_model->vertices[0].p.v[2] = 0.0f;
gBlend_model->vertices[1].p.v[0] = -1.0f;
gBlend_model->vertices[1].p.v[1] = -1.0f;
gBlend_model->vertices[1].p.v[2] = 0.0f;
gBlend_model->vertices[2].p.v[0] = 1.0f;
gBlend_model->vertices[2].p.v[1] = -1.0f;
gBlend_model->vertices[2].p.v[2] = 0.0f;
gBlend_model->vertices[3].p.v[0] = 1.0f;
gBlend_model->vertices[3].p.v[1] = 1.0f;
gBlend_model->vertices[3].p.v[2] = 0.0f;
gBlend_material->flags = BR_MATF_PERSPECTIVE | BR_MATF_SMOOTH;
gBlend_material->flags |= (BR_MATF_LIGHT | BR_MATF_PRELIT);
gBlend_model->flags |= BR_MODF_KEEP_ORIGINAL;
gBlend_material->extra_prim = fadealpha;
PathCat(path, gApplication_path, "PIXELMAP");
PathCat(path, path, "SMOKE.PIX");
gBlend_material->colour_map = DRPixelmapLoad(path);
if (!gBlend_material->colour_map) {
FatalError(79, path);
}
gBlend_material->colour_map->map = gRender_palette;
BrMapAdd(gBlend_material->colour_map);
gBlend_model->vertices[0].map.v[0] = 0.0f;
gBlend_model->vertices[0].map.v[1] = 1.0f - 1.0f / (float)gBlend_material->colour_map->height;
gBlend_model->vertices[1].map.v[0] = 0.0f;
gBlend_model->vertices[1].map.v[1] = 0.0f;
gBlend_model->vertices[2].map.v[0] = 1.0f - 1.0f / (float)gBlend_material->colour_map->width;
gBlend_model->vertices[2].map.v[1] = 0.0f;
gBlend_model->vertices[3].map.v[0] = 1.0f - 1.0f / (float)gBlend_material->colour_map->width;
gBlend_model->vertices[3].map.v[1] = 1.0f - 1.0f / (float)gBlend_material->colour_map->height;
BrModelAdd(gBlend_model);
BrMaterialAdd(gBlend_material);
BrActorAdd(gDont_render_actor, gBlend_actor);
}
// IDA: void __cdecl Init2DStuff()
void Init2DStuff(void) {
br_camera* camera;
static br_token_value fadealpha[3];
static br_token_value fadealpha[3] = { { BRT_BLEND_B, { .u32 = 1u } }, { BRT_OPACITY_X, { .x = 0x800000 } }, { 0 } };
tPath_name path;
br_scalar prat_u;
br_scalar prat_v;
LOG_TRACE("()");
NOT_IMPLEMENTED();
g2d_camera = BrActorAllocate(BR_ACTOR_CAMERA, NULL);
gDim_model = BrModelAllocate("gDim_model", 4, 2);
gDim_material = BrMaterialAllocate("gDim_material");
gDim_actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
gPrat_model = BrModelAllocate("gPrat_model", 4, 2);
gPrat_material = BrMaterialAllocate("gPrat_material");
gPrat_actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
if (!gDim_model || !gDim_material || !gDim_actor || !gPrat_model || !gPrat_material || !gPrat_actor || !g2d_camera) {
FatalError(94);
}
g2d_camera->identifier = "g2d_camera";
camera = g2d_camera->type_data;
camera->type = BR_CAMERA_PARALLEL;
camera->hither_z = 1.0f;
camera->yon_z = 3.0f;
camera->width = gScreen->width;
camera->height = gScreen->height;
gDim_actor->identifier = "gDim_actor";
gDim_actor->model = gDim_model;
gDim_actor->material = gDim_material;
gDim_model->faces->vertices[0] = 0;
gDim_model->faces->vertices[1] = 1;
gDim_model->faces->vertices[2] = 2;
gDim_model->faces[1].vertices[0] = 2;
gDim_model->faces[1].vertices[1] = 3;
gDim_model->faces[1].vertices[2] = 0;
gDim_model->vertices->p.v[0] = 150.0f;
gDim_model->vertices->p.v[1] = -20.0f;
gDim_model->vertices->p.v[2] = -2.0f;
gDim_model->vertices[1].p.v[0] = 150.0f;
gDim_model->vertices[1].p.v[1] = -100.0f;
gDim_model->vertices[1].p.v[2] = -2.0f;
gDim_model->vertices[2].p.v[0] = 200.0f;
gDim_model->vertices[2].p.v[1] = -100.0f;
gDim_model->vertices[2].p.v[2] = -2.0f;
gDim_model->vertices[3].p.v[0] = 200.0f;
gDim_model->vertices[3].p.v[1] = -20.0f;
gDim_model->vertices[3].p.v[2] = -2.0f;
gDim_material->colour = 0;
gDim_material->flags = BR_MATF_FORCE_FRONT;
gDim_model->flags |= BR_MODF_KEEP_ORIGINAL;
gDim_material->extra_prim = fadealpha;
BrModelAdd(gDim_model);
BrMaterialAdd(gDim_material);
BrActorAdd(g2d_camera, gDim_actor);
gDim_actor->render_style = BR_RSTYLE_NONE;
gPrat_actor->identifier = "gPrat_actor";
gPrat_actor->model = gPrat_model;
gPrat_actor->material = gPrat_material;
gPrat_model->faces->vertices[0] = 0;
gPrat_model->faces->vertices[1] = 1;
gPrat_model->faces->vertices[2] = 2;
gPrat_model->faces[1].vertices[0] = 2;
gPrat_model->faces[1].vertices[1] = 3;
gPrat_model->faces[1].vertices[2] = 0;
gPrat_model->vertices->p.v[0] = 150.0f;
gPrat_model->vertices->p.v[1] = -20.0f;
gPrat_model->vertices->p.v[2] = -2.0f;
gPrat_model->vertices[1].p.v[0] = 150.0f;
gPrat_model->vertices[1].p.v[1] = -100.0f;
gPrat_model->vertices[1].p.v[2] = -2.0f;
gPrat_model->vertices[2].p.v[0] = 200.0f;
gPrat_model->vertices[2].p.v[1] = -100.0f;
gPrat_model->vertices[2].p.v[2] = -2.0f;
gPrat_model->vertices[3].p.v[0] = 200.0f;
gPrat_model->vertices[3].p.v[1] = -20.0f;
gPrat_model->vertices[3].p.v[2] = -2.0f;
gPrat_material->colour = 0xFFFFFF;
gPrat_material->flags = BR_MATF_FORCE_FRONT;
gPrat_model->flags |= BR_MODF_KEEP_ORIGINAL;
prat_u = 104.0f / (float)HighResPratBufferWidth();
prat_v = 110.0f / (float)HighResPratBufferHeight();
gPrat_model->vertices->map.v[0] = 0.0f;
gPrat_model->vertices->map.v[1] = 0.0f;
gPrat_model->vertices[1].map.v[0] = 0.0f;
gPrat_model->vertices[1].map.v[1] = prat_v;
gPrat_model->vertices[2].map.v[0] = prat_u;
gPrat_model->vertices[2].map.v[1] = prat_v;
gPrat_model->vertices[3].map.v[0] = prat_u;
gPrat_model->vertices[3].map.v[1] = 0.0f;
BrModelAdd(gPrat_model);
BrMaterialAdd(gPrat_material);
BrActorAdd(g2d_camera, gPrat_actor);
gPrat_actor->render_style = BR_RSTYLE_NONE;
}
// IDA: void __usercall InitialiseApplication(int pArgc@<EAX>, char **pArgv@<EDX>)
@ -378,8 +567,22 @@ void InitialiseApplication(int pArgc, char** pArgv) {
InitBRFonts();
LoadMiscStrings();
LoadInRegistees();
#ifdef DETHRACE_3DFX_PATCH
// dethrace: if statement added to support all types of games
if (harness_game_config.opengl_3dfx_mode) {
InitLineStuff();
InitSmokeStuff();
Init2DStuff();
}
#endif
FinishLoadingGeneral();
#ifndef DETHRACE_3DFX_PATCH
// 3dfx patch calls this earlier
InitializePalettes();
#endif
AustereWarning();
LoadInterfaceStrings();
InitializeActionReplay();
@ -391,6 +594,14 @@ void InitialiseApplication(int pArgc, char** pArgv) {
gDefault_track_material = BrMaterialAllocate("gDefault_track_material");
gDefault_track_material->index_base = 227;
gDefault_track_material->index_range = 1;
#ifdef DETHRACE_3DFX_PATCH
gDefault_track_material->ka = 1.0;
gDefault_track_material->kd = 0.0;
gDefault_track_material->ks = 0.0;
gDefault_track_material->colour = ((br_colour*)gRender_palette->pixels)[227];
#endif
BrMaterialAdd(gDefault_track_material);
InitShadow();
InitFlics();
@ -423,9 +634,8 @@ void InitialiseApplication(int pArgc, char** pArgv) {
// IDA: void __usercall InitialiseDeathRace(int pArgc@<EAX>, char **pArgv@<EDX>)
void InitialiseDeathRace(int pArgc, char** pArgv) {
PDInitialiseSystem();
InitialiseApplication(pArgc, pArgv);
// dword_112DF8 = 1; // never checked by game
gInitialisation_finished = 1;
}
// IDA: void __usercall InitGame(int pStart_race@<EAX>)
@ -473,6 +683,10 @@ void InitGame(int pStart_race) {
gProgram_state.redo_race_index = -1;
gWait_for_it = 0;
SwitchToLoresMode();
// added by dethrace to support --game-completed arg
gProgram_state.game_completed = harness_game_config.game_completed;
// -
}
// IDA: void __cdecl DisposeGameIfNecessary()
@ -594,6 +808,23 @@ void InitRace(void) {
}
PrintMemoryDump(0, "DIRECTLY AFTER LOADING IN TRACK");
LoadCopCars();
#ifdef DETHRACE_3DFX_PATCH
// In the 3dfx patch this code was moved from `LoadTrack` so that the pedestrian material would be
// fogged correctly
InstantDepthChange(
gProgram_state.default_depth_effect.type,
gProgram_state.default_depth_effect.sky_texture,
gProgram_state.default_depth_effect.start,
gProgram_state.default_depth_effect.end);
gSwap_sky_texture = 0;
if (!GetSkyTextureOn()) {
ToggleSkyQuietly();
}
gSwap_depth_effect_type = -1;
if (!GetDepthCueingOn()) {
ToggleDepthCueingQuietly();
}
#endif
PrintMemoryDump(0, "AFTER LOADING IN COPS");
SaveShadeTables();
gCountdown = 7;
@ -669,6 +900,15 @@ void DisposeRace(void) {
PossibleService();
DisposePratcam();
PossibleService();
#ifdef DETHRACE_FIX_BUGS
// when exiting a race, skid mark materials are unloaded, but material_modifiers is not changed.
// In 3dfx mode, `FrobFog` is called during loading the next track, which iterates over the material_modifiers
// causing a use-after-free
for (int i = 0; i < COUNT_OF(gCurrent_race.material_modifiers); i++) {
gCurrent_race.material_modifiers[i].skid_mark_material = NULL;
}
#endif
}
// IDA: int __cdecl GetScreenSize()

View File

@ -17,6 +17,7 @@
#include "flicplay.h"
#include "formats.h"
#include "globvars.h"
#include "globvrbm.h"
#include "globvrkm.h"
#include "globvrpb.h"
#include "grafdata.h"
@ -562,7 +563,13 @@ br_material* LoadMaterial(char* pName) {
PossibleService();
PathCat(the_path, gApplication_path, "MATERIAL");
PathCat(the_path, the_path, pName);
return BrMaterialLoad(the_path);
result = BrMaterialLoad(the_path);
#ifdef DETHRACE_3DFX_PATCH
if (result != NULL) {
GlorifyMaterial(&result, 1);
}
#endif
return result;
}
// IDA: br_model* __usercall LoadModel@<EAX>(char *pName@<EAX>)
@ -574,7 +581,11 @@ br_model* LoadModel(char* pName) {
PossibleService();
PathCat(the_path, gApplication_path, "MODELS");
PathCat(the_path, the_path, pName);
return BrModelLoad(the_path);
model = BrModelLoad(the_path);
#ifdef DETHRACE_3DFX_PATCH
WhitenVertexRGB(&model, 1);
#endif
return model;
}
// IDA: br_actor* __usercall LoadActor@<EAX>(char *pName@<EAX>)
@ -637,6 +648,9 @@ void DRLoadMaterials(char* pPath_name) {
PossibleService();
number_of_materials = BrMaterialLoadMany(pPath_name, material_array, COUNT_OF(material_array));
#ifdef DETHRACE_3DFX_PATCH
GlorifyMaterial(material_array, number_of_materials);
#endif
BrMaterialAddMany(material_array, number_of_materials);
}
@ -648,6 +662,9 @@ void DRLoadModels(char* pPath_name) {
PossibleService();
number_of_models = BrModelLoadMany(pPath_name, model_array, COUNT_OF(model_array));
#ifdef DETHRACE_3DFX_PATCH
WhitenVertexRGB(model_array, number_of_models);
#endif
BrModelAddMany(model_array, number_of_models);
}
@ -698,6 +715,9 @@ void LoadInRegisteeDir(char* pThe_dir_path) {
PathCat(reg_path, pThe_dir_path, "REG");
LoadInFiles(reg_path, "PALETTES", DRLoadPalette);
LoadInFiles(reg_path, "SHADETAB", DRLoadShadeTable);
#ifdef DETHRACE_3DFX_PATCH
InitializePalettes();
#endif
LoadInFiles(reg_path, "PIXELMAP", DRLoadPixelmaps);
LoadInFiles(reg_path, "MATERIAL", DRLoadMaterials);
LoadInFiles(reg_path, "MODELS", DRLoadModels);
@ -812,7 +832,60 @@ tS8* ConvertPixTo16BitStripMap(br_pixelmap* pBr_map) {
tU8 byte;
tU16* palette_entry;
LOG_TRACE("(%p)", pBr_map);
NOT_IMPLEMENTED();
palette_entry = PaletteOf16Bits(gRender_palette)->pixels;
max_line_bytes = 125 * ((pBr_map->width + 61) / 62) + 2;
new_line = BrMemAllocate(max_line_bytes, kMem_strip_image);
temp_strip_image = BrMemAllocate(max_line_bytes * pBr_map->height, kMem_strip_image);
current_size = 2;
i = 0;
*(tU16*)temp_strip_image = pBr_map->height;
for (i = 0; i < pBr_map->height; i++) {
new_line_length = 2;
counting_blanks = 0;
next_byte = (tU8*)pBr_map->pixels + i * pBr_map->row_bytes;
if (*next_byte == 0) {
counting_blanks = 1;
}
counter = 0;
chunk_counter = 0;
j = 0;
while (1) {
while (counter < 62) {
if (j == pBr_map->width)
break;
byte = *next_byte;
if (counting_blanks != (*next_byte == 0))
break;
if (!counting_blanks) {
*(tU16*)&new_line[new_line_length] = palette_entry[byte];
new_line_length += 2;
}
next_byte++;
counter++;
j++;
}
if (counting_blanks) {
new_line[new_line_length - 1] = 2 * counter;
} else {
new_line[new_line_length - 2 * counter - 1] = -2 * counter;
}
counting_blanks = byte == 0;
++chunk_counter;
counter = 0;
if (j == pBr_map->width) {
break;
}
new_line_length++;
}
*new_line = chunk_counter;
memcpy(temp_strip_image + current_size, new_line, new_line_length);
current_size += new_line_length;
}
strip_image = BrMemAllocate(current_size, kMem_strip_image_perm);
memcpy(strip_image, temp_strip_image, current_size);
BrMemFree(temp_strip_image);
return (tS8*)strip_image;
}
// IDA: tS8* __usercall ConvertPixToStripMap@<EAX>(br_pixelmap *pThe_br_map@<EAX>)
@ -845,7 +918,7 @@ tS8* ConvertPixToStripMap(br_pixelmap* pThe_br_map) {
temp_strip_image = BrMemAllocate(pThe_br_map->row_bytes * pThe_br_map->height, kMem_strip_image);
current_size = 2;
*(br_uint_16*)temp_strip_image = pThe_br_map->height;
*(tU16*)temp_strip_image = pThe_br_map->height;
current_strip_pointer = temp_strip_image;
for (i = 0; i < pThe_br_map->height; i++) {
@ -1703,14 +1776,19 @@ void SetModelFlags(br_model* pModel, int pOwner) {
#else
if (pOwner == OPPONENT_APC_IDX || gAusterity_mode) {
#endif
if ((pModel->flags & BR_MODF_UPDATEABLE) != 0) {
pModel->flags &= ~(BR_MODF_KEEP_ORIGINAL | BR_MODF_UPDATEABLE);
BrModelUpdate(pModel, BR_MODU_ALL);
#ifdef DETHRACE_3DFX_PATCH
if (!gMaterial_fogging)
#endif
{
if ((pModel->flags & BR_MODF_UPDATEABLE) != 0) {
pModel->flags &= ~(BR_MODF_KEEP_ORIGINAL | BR_MODF_UPDATEABLE);
BrModelUpdate(pModel, BR_MODU_ALL);
}
return;
}
} else {
pModel->flags |= BR_MODF_DONT_WELD | BR_MODF_KEEP_ORIGINAL | BR_MODF_UPDATEABLE;
BrModelUpdate(pModel, BR_MODU_ALL);
}
pModel->flags |= BR_MODF_DONT_WELD | BR_MODF_KEEP_ORIGINAL | BR_MODF_UPDATEABLE;
BrModelUpdate(pModel, BR_MODU_ALL);
}
}
@ -1834,9 +1912,17 @@ void LoadCar(char* pCar_name, tDriver pDriver, tCar_spec* pCar_spec, int pOwner,
pCar_spec->cockpit_images[j] = NULL;
} else {
the_image = LoadPixelmap(str);
if (the_image == NULL)
if (the_image == NULL) {
FatalError(kFatalError_LoadCockpitImage);
pCar_spec->cockpit_images[j] = ConvertPixToStripMap(the_image);
}
#ifdef DETHRACE_3DFX_PATCH
if (gBack_screen->type == BR_PMT_RGB_565) {
pCar_spec->cockpit_images[j] = ConvertPixTo16BitStripMap(the_image);
} else
#endif
{
pCar_spec->cockpit_images[j] = ConvertPixToStripMap(the_image);
}
BrPixelmapFree(the_image);
}
GetALineAndDontArgue(g, s);

View File

@ -39,10 +39,30 @@ void QuitGame(void) {
DRS3ShutDown();
}
if (gBr_initialized) {
#ifdef DETHRACE_FIX_BUGS
// In 3dfx mode, we need direct pixel access before calling `ClearEntireScreen`
if (harness_game_config.opengl_3dfx_mode) {
PDLockRealBackScreen(1);
}
#endif
ClearEntireScreen();
}
PDRevertPalette();
StopMusic();
if (gBrZb_initialized) {
BrZbEnd();
}
if (gBr_initialized) {
BrV1dbEndWrapper();
}
#ifdef DETHRACE_FIX_BUGS
// Hack: not sure if this is a bug in the original code or if its something caused by dethrace.
// Avoids the device screen pixelmap being double-freed
gDOSGfx_initialized = 0;
#endif
PDShutdownSystem();
CloseDiagnostics();
exit(0);

View File

@ -2,8 +2,9 @@
#define _MAIN_H_
#include "dr_types.h"
#include "harness/compiler.h"
void QuitGame(void);
void HARNESS_NORETURN QuitGame(void);
tU32 TrackCount(br_actor* pActor, tU32* pCount);

View File

@ -590,6 +590,14 @@ tRace_result MainGameLoop(void) {
EnterUserMessage();
SkidsPerFrame();
if (!gWait_for_it) {
#if defined(DETHRACE_3DFX_PATCH) && defined(DETHRACE_FIX_BUGS)
// Fixes issue where returning to race mode from the UI shows 2d elements in the wrong colors for half a second.
// In 3dfx mode, 2d elements are rendered using `Copy8BitTo16BitRectangleWithTransparency` which uses
// `gCurrent_palette` to convert 8 bit to 16 bit pixels. `gCurrent_palette` is still set to the interface palette here
// I couldn't confirm why this does not happen in the original 3dfx executable (or does it?)
EnsureRenderPalette();
EnsurePaletteUp();
#endif
RenderAFrame(1);
}
CheckReplayTurnOn();

View File

@ -3,6 +3,7 @@
#include "controls.h"
#include "flicplay.h"
#include "globvars.h"
#include "globvrbm.h"
#include "globvrpb.h"
#include "grafdata.h"
#include "graphics.h"
@ -437,6 +438,7 @@ int QuitVerifyDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pE
if (pCurrent_choice) {
memcpy(gBack_screen->pixels, gPixels_copy__mainmenu, gPixel_buffer_size__mainmenu);
memcpy(gCurrent_palette_pixels, gPalette_copy__mainmenu, 0x400u);
g16bit_palette_valid = 0;
} else {
ClearEntireScreen();
}

View File

@ -7,6 +7,7 @@
#include "displays.h"
#include "errors.h"
#include "globvars.h"
#include "globvrbm.h"
#include "globvrpb.h"
#include "grafdata.h"
#include "graphics.h"
@ -1531,6 +1532,7 @@ void ReceivedGameplay(tNet_contents* pContents, tNet_message* pMessage, tU32 pRe
FadePaletteDown();
memcpy(gBack_screen->pixels, gPixels_copy, gPixel_buffer_size);
memcpy(gCurrent_palette_pixels, gPalette_copy, 1024);
g16bit_palette_valid = 0;
BrMemFree(gPixels_copy);
BrMemFree(gPalette_copy);
PDScreenBufferSwap(0);

View File

@ -5,6 +5,7 @@
#include "displays.h"
#include "errors.h"
#include "globvars.h"
#include "globvrbm.h"
#include "globvrpb.h"
#include "graphics.h"
#include "harness/hooks.h"
@ -1296,6 +1297,9 @@ void NetFullScreenMessage(int pStr_index, int pLeave_it_up_there) {
if (restore_screen) {
memcpy(gBack_screen->pixels, gPixels_copy_, gPixel_buffer_size_);
memcpy(gCurrent_palette_pixels, gPalette_copy_, 0x400u);
#ifdef DETHRACE_3DFX_PATCH
g16bit_palette_valid = 0;
#endif
BrMemFree(gPixels_copy_);
BrMemFree(gPalette_copy_);
PDScreenBufferSwap(0);
@ -1577,13 +1581,11 @@ void ReceivedConfirm(tNet_contents* pContents) {
// IDA: void __usercall ReceivedDisableCar(tNet_contents *pContents@<EAX>)
void ReceivedDisableCar(tNet_contents* pContents) {
LOG_TRACE("(%p)", pContents);
}
// IDA: void __usercall ReceivedEnableCar(tNet_contents *pContents@<EAX>)
void ReceivedEnableCar(tNet_contents* pContents) {
LOG_TRACE("(%p)", pContents);
}
// IDA: void __usercall ReceivedScores(tNet_contents *pContents@<EAX>)

View File

@ -45,6 +45,9 @@ void InitOilSpills(void) {
the_material->colour_map = NULL;
BrMatrix23Identity(&the_material->map_transform);
the_material->index_shade = BrTableFind("IDENTITY.TAB");
#ifdef DETHRACE_3DFX_PATCH
GlorifyMaterial(&the_material, 1);
#endif
BrMaterialUpdate(the_material, BR_MATU_ALL);
the_model = BrModelAllocate(NULL, 4, 2);
the_model->flags |= BR_MODF_KEEP_ORIGINAL;

View File

@ -4,6 +4,7 @@
#include "errors.h"
#include "flicplay.h"
#include "globvars.h"
#include "globvrbm.h"
#include "globvrpb.h"
#include "grafdata.h"
#include "graphics.h"
@ -313,14 +314,28 @@ void PratcamEvent(int pIndex) {
int HighResPratBufferWidth(void) {
int prat_width;
LOG_TRACE("()");
NOT_IMPLEMENTED();
if (!gDevious_2d || !gTextures_need_powers_of_2) {
return 104;
}
for (prat_width = 1; prat_width < 104; prat_width *= 2) {
;
}
return prat_width;
}
// IDA: int __cdecl HighResPratBufferHeight()
int HighResPratBufferHeight(void) {
int prat_height;
LOG_TRACE("()");
NOT_IMPLEMENTED();
if (!gDevious_2d || !gTextures_need_powers_of_2) {
return 110;
}
for (prat_height = 1; prat_height < 110; prat_height *= 2) {
;
}
return prat_height;
}
// IDA: void __cdecl InitPratcam()
@ -339,7 +354,11 @@ void InitPratcam(void) {
the_pixels = BrMemAllocate(52 * 46, kMem_pratcam_pixelmap);
break;
case 1:
#ifdef DETHRACE_3DFX_PATCH
the_pixels = BrMemAllocate(HighResPratBufferWidth() * HighResPratBufferHeight(), kMem_pratcam_pixelmap);
#else
the_pixels = BrMemAllocate(104 * 110, kMem_pratcam_pixelmap);
#endif
break;
default:
TELL_ME_IF_WE_PASS_THIS_WAY();
@ -352,7 +371,11 @@ void InitPratcam(void) {
gPrat_buffer = DRPixelmapAllocate(gScreen->type, 52, 46, the_pixels, 0);
break;
case 1:
#ifdef DETHRACE_3DFX_PATCH
gPrat_buffer = DRPixelmapAllocate(BR_PMT_INDEX_8, HighResPratBufferWidth(), HighResPratBufferHeight(), the_pixels, 0);
#else
gPrat_buffer = DRPixelmapAllocate(gScreen->type, 104, 110, the_pixels, 0);
#endif
break;
default:
TELL_ME_IF_WE_PASS_THIS_WAY();
@ -420,15 +443,15 @@ void DoPratcam(tU32 pThe_time) {
tU32 time_diff;
tU32 old_last_time;
br_pixelmap* the_image;
br_pixelmap* left_image;
br_pixelmap* right_image;
br_pixelmap* left_image;
LOG_TRACE("(%d)", pThe_time);
if (gAusterity_mode) {
return;
}
left_image = gProgram_state.current_car.prat_cam_right;
right_image = gProgram_state.current_car.prat_cam_left;
right_image = gProgram_state.current_car.prat_cam_right;
left_image = gProgram_state.current_car.prat_cam_left;
y_offset = (gNet_mode == eNet_mode_none) ? 0 : gCurrent_graf_data->net_head_box_bot + 1;
right_hand = gProgram_state.current_car.prat_left <= gBack_screen->width / 2;
@ -461,6 +484,7 @@ void DoPratcam(tU32 pThe_time) {
if (right_hand) {
offset = -offset;
}
DontLetFlicFuckWithPalettes();
DisableTranslationText();
for (i = 0; i < (old_last_time != 0 ? ((pThe_time - old_last_time) / gPrat_flic.frame_period) : 1); i++) {
@ -472,12 +496,49 @@ void DoPratcam(tU32 pThe_time) {
}
EnableTranslationText();
LetFlicFuckWithPalettes();
BrPixelmapRectangleCopy(gBack_screen,
#ifdef DETHRACE_3DFX_PATCH
PDUnlockRealBackScreen(1);
if (gDevious_2d) {
gPrat_model->vertices[1].p.v[0] = gProgram_state.current_car.prat_left + offset;
gPrat_model->vertices[0].p.v[0] = gProgram_state.current_car.prat_left + offset;
gPrat_model->vertices[3].p.v[1] = -(y_offset + gProgram_state.current_car.prat_top);
gPrat_model->vertices[0].p.v[1] = -(y_offset + gProgram_state.current_car.prat_top);
gPrat_model->vertices[3].p.v[0] = gPrat_model->vertices[1].p.v[0] + 104.0f;
gPrat_model->vertices[2].p.v[0] = gPrat_model->vertices[1].p.v[0] + 104.0f;
gPrat_model->vertices[2].p.v[1] = gPrat_model->vertices[3].p.v[1] - 110.0f;
gPrat_model->vertices[1].p.v[1] = gPrat_model->vertices[3].p.v[1] - 110.0f;
BrModelUpdate(gPrat_model, BR_MODU_VERTEX_POSITIONS);
gPrat_actor->render_style = BR_RSTYLE_FACES;
gPrat_material->colour_map = gPrat_buffer;
gPrat_buffer->map = gRender_palette;
BrMapAdd(gPrat_buffer);
BrMaterialUpdate(gPrat_material, BR_MATU_ALL);
BrZbSceneRender(g2d_camera, g2d_camera, gBack_screen, gDepth_buffer);
BrMapRemove(gPrat_buffer);
gPrat_actor->render_style = BR_RSTYLE_NONE;
} else {
DRPixelmapRectangleCopy(
gBack_screen,
gProgram_state.current_car.prat_left + offset,
gProgram_state.current_car.prat_top + y_offset,
gPrat_buffer,
0, 0,
gPrat_buffer->width, gPrat_buffer->height);
}
PDLockRealBackScreen(1);
#else
BrPixelmapRectangleCopy(
gBack_screen,
gProgram_state.current_car.prat_left + offset,
gProgram_state.current_car.prat_top + y_offset,
gPrat_buffer,
0, 0,
gPrat_buffer->width, gPrat_buffer->height);
#endif
if (gProgram_state.current_car.prat_cam_top != NULL) {
top_border_height = gProgram_state.current_car.prat_cam_top->height;
DRPixelmapRectangleMaskedCopy(
@ -491,22 +552,22 @@ void DoPratcam(tU32 pThe_time) {
} else {
top_border_height = 0;
}
if (right_image != NULL) {
DRPixelmapRectangleMaskedCopy(gBack_screen,
gProgram_state.current_car.prat_left - right_image->width + offset,
gProgram_state.current_car.prat_top - top_border_height + y_offset,
right_image,
0, 0,
right_image->width, right_image->height);
}
if (left_image != NULL) {
DRPixelmapRectangleMaskedCopy(gBack_screen,
gProgram_state.current_car.prat_left - left_image->width + offset,
gProgram_state.current_car.prat_top - top_border_height + y_offset,
left_image,
0, 0,
left_image->width, left_image->height);
}
if (right_image != NULL) {
DRPixelmapRectangleMaskedCopy(
gBack_screen,
gProgram_state.current_car.prat_right + offset - 1,
gProgram_state.current_car.prat_top - top_border_height - 1 + y_offset,
left_image,
right_image,
0, 0,
left_image->width, left_image->height);
right_image->width, right_image->height);
}
if (gProgram_state.current_car.prat_cam_bottom != NULL) {
DRPixelmapRectangleMaskedCopy(

View File

@ -113,7 +113,12 @@ void DrawRaceList(int pOffset) {
gBig_font,
gRace_list[i].name);
if (gRace_list[i].been_there_done_that && gBullet_image != NULL) {
BrPixelmapRectangleCopy(gBack_screen,
#ifdef DETHRACE_3DFX_PATCH
DRPixelmapRectangleCopy(
#else
BrPixelmapRectangleCopy(
#endif
gBack_screen,
gCurrent_graf_data->choose_race_bullet_left,
y + (gBig_font->glyph_y - gBullet_image->height) / 2,
gBullet_image,
@ -2022,7 +2027,11 @@ void ChallengeStart(void) {
BrFatal("C:\\Msdev\\Projects\\DethRace\\Racestrt.c", 2610, "Bruce bug at line %d, file C:\\Msdev\\Projects\\DethRace\\Racestrt.c", 50);
}
the_map = DRPixelmapAllocate(
#ifdef DETHRACE_3DFX_PATCH
BR_PMT_INDEX_8,
#else
gScreen->type,
#endif
gCurrent_graf_data->dare_mugshot_width,
gCurrent_graf_data->dare_mugshot_height,
0,
@ -2032,7 +2041,16 @@ void ChallengeStart(void) {
DisposeFlicPanel(0);
TellyInImage(the_map, gCurrent_graf_data->dare_mugshot_left, gCurrent_graf_data->dare_mugshot_top);
BrPixelmapFree(the_map);
the_map = DRPixelmapAllocate(gScreen->type, gCurrent_graf_data->dare_text_width, gCurrent_graf_data->dare_mugshot_height, 0, 0);
the_map = DRPixelmapAllocate(
#ifdef DETHRACE_3DFX_PATCH
BR_PMT_INDEX_8,
#else
gScreen->type,
#endif
gCurrent_graf_data->dare_text_width,
gCurrent_graf_data->dare_mugshot_height,
0,
0);
BrPixelmapFill(the_map, 0);
TransBrPixelmapText(the_map, 0, 0, 1u, gBig_font, gOpponents[gChallenger_index__racestrt].abbrev_name);
PathCat(the_path, gApplication_path, "DARES.TXT");

View File

@ -544,20 +544,42 @@ void BuildWrecks(void) {
position += 1;
}
}
gWreck_render_area = BrPixelmapAllocateSub(
gBack_screen,
gCurrent_graf_data->wreck_render_x,
gCurrent_graf_data->wreck_render_y,
gCurrent_graf_data->wreck_render_w,
gCurrent_graf_data->wreck_render_h);
#ifdef DETHRACE_3DFX_PATCH
if (gScreen->type == BR_PMT_INDEX_8) {
#endif
gWreck_render_area = BrPixelmapAllocateSub(
gBack_screen,
gCurrent_graf_data->wreck_render_x,
gCurrent_graf_data->wreck_render_y,
gCurrent_graf_data->wreck_render_w,
gCurrent_graf_data->wreck_render_h);
#ifdef DETHRACE_3DFX_PATCH
} else {
gWreck_render_area = BrPixelmapAllocateSub(
gReal_back_screen,
gCurrent_graf_data->wreck_render_x * 2,
gCurrent_graf_data->wreck_render_y * 2 + HIRES_Y_OFFSET,
gCurrent_graf_data->wreck_render_w * 2,
gCurrent_graf_data->wreck_render_h * 2);
}
#endif
gWreck_render_area->origin_x = gWreck_render_area->width / 2;
gWreck_render_area->origin_y = gWreck_render_area->height / 2;
gWreck_z_buffer = BrPixelmapAllocateSub(
gDepth_buffer,
gCurrent_graf_data->wreck_render_x,
gCurrent_graf_data->wreck_render_y,
gCurrent_graf_data->wreck_render_w,
gCurrent_graf_data->wreck_render_h);
#ifdef DETHRACE_3DFX_PATCH
if (gScreen->type == BR_PMT_INDEX_8) {
#endif
gWreck_z_buffer = BrPixelmapAllocateSub(
gDepth_buffer,
gCurrent_graf_data->wreck_render_x,
gCurrent_graf_data->wreck_render_y,
gCurrent_graf_data->wreck_render_w,
gCurrent_graf_data->wreck_render_h);
#ifdef DETHRACE_3DFX_PATCH
} else {
gWreck_z_buffer = gDepth_buffer;
}
#endif
}
// IDA: void __cdecl DisposeWrecks()
@ -595,9 +617,14 @@ void DisposeWrecks(void) {
BrActorFree(gWreck_root);
BrActorFree(gWreck_camera);
gWreck_render_area->pixels = NULL;
gWreck_z_buffer->pixels = NULL;
BrPixelmapFree(gWreck_render_area);
BrPixelmapFree(gWreck_z_buffer);
#ifdef DETHRACE_3DFX_PATCH
if (gScreen->type == BR_PMT_INDEX_8) {
gWreck_z_buffer->pixels = NULL;
BrPixelmapFree(gWreck_z_buffer);
}
#endif
}
// IDA: int __usercall MatrixIsIdentity@<EAX>(br_matrix34 *pMat@<EAX>)
@ -715,7 +742,7 @@ int CastSelectionRay(int* pCurrent_choice, int* pCurrent_mode) {
GetMousePosition(&mouse_x, &mouse_y);
if (gReal_graf_data_index != 0) {
mouse_x = 2 * mouse_x;
mouse_y = 2 * mouse_y + 40;
mouse_y = 2 * mouse_y + HIRES_Y_OFFSET;
}
for (i = 0; i < gWreck_count; i++) {
BrMatrix34PreScale(&gWreck_array[i].actor->t.t.mat, 2.f, 2.f, 2.f);
@ -808,51 +835,115 @@ void DamageScrnDraw(int pCurrent_choice, int pCurrent_mode) {
}
EnsureRenderPalette();
EnsurePaletteUp();
BrPixelmapFill(gWreck_z_buffer, 0xffffffff);
BrPixelmapFill(gWreck_render_area, BR_COLOUR_RGBA(0xb0, 0xb0, 0xb0, 0xb0));
#ifdef DETHRACE_3DFX_PATCH
if (gScreen->type == BR_PMT_INDEX_8) {
#endif
BrPixelmapFill(gWreck_z_buffer, 0xffffffff);
BrPixelmapFill(gWreck_render_area, BR_COLOUR_RGBA(0xb0, 0xb0, 0xb0, 0xb0));
rows = gWreck_render_area->height / 15.f;
columns = gWreck_render_area->width / 15.f;
for (v = 0; v <= rows; v++) {
BrPixelmapLine(gWreck_render_area,
-gWreck_render_area->origin_x,
gWreck_render_area->height / 2.f - 15.f * (rows / 2.f - v) - gWreck_render_area->origin_y,
gWreck_render_area->width - gWreck_render_area->origin_x,
gWreck_render_area->height / 2.f - 15.f * (rows / 2.f - v) - gWreck_render_area->origin_y,
8);
}
for (h = 0; h <= columns; h++) {
BrPixelmapLine(gWreck_render_area,
gWreck_render_area->width / 2.f - 15.f * (columns / 2.f - h) - gWreck_render_area->origin_x,
-gWreck_render_area->origin_y,
gWreck_render_area->width / 2.f - 15.f * (columns / 2.f - h) - gWreck_render_area->origin_x,
gWreck_render_area->height - gWreck_render_area->origin_y,
8);
}
BrZbSceneRenderBegin(gUniverse_actor, gWreck_camera, gWreck_render_area, gWreck_z_buffer);
BrZbSceneRenderAdd(gWreck_root);
BrZbSceneRenderEnd();
if (sel_actor != NULL) {
BrActorRemove(sel_actor);
sel_actor->model = NULL;
BrActorFree(sel_actor);
}
BrPixelmapRectangleFill(gBack_screen,
gCurrent_graf_data->wreck_name_left,
gCurrent_graf_data->wreck_name_top,
gCurrent_graf_data->wreck_name_right - gCurrent_graf_data->wreck_name_left,
gCurrent_graf_data->wreck_name_bottom - gCurrent_graf_data->wreck_name_top,
0);
if (gWreck_selected >= 0 && (gWreck_zoomed_in >= 0 || pCurrent_mode == 0)) {
name = GetDriverName(gWreck_array[gWreck_selected].car_type,
gWreck_array[gWreck_selected].car_index);
TransBrPixelmapText(gBack_screen,
(gCurrent_graf_data->wreck_name_left + gCurrent_graf_data->wreck_name_right - BrPixelmapTextWidth(gBack_screen, gFont_7, name)) / 2,
gCurrent_graf_data->wreck_name_base_line,
84,
gFont_7,
name);
rows = gWreck_render_area->height / 15.f;
columns = gWreck_render_area->width / 15.f;
for (v = 0; v <= rows; v++) {
BrPixelmapLine(gWreck_render_area,
-gWreck_render_area->origin_x,
gWreck_render_area->height / 2.f - 15.f * (rows / 2.f - v) - gWreck_render_area->origin_y,
gWreck_render_area->width - gWreck_render_area->origin_x,
gWreck_render_area->height / 2.f - 15.f * (rows / 2.f - v) - gWreck_render_area->origin_y,
8);
}
for (h = 0; h <= columns; h++) {
BrPixelmapLine(gWreck_render_area,
gWreck_render_area->width / 2.f - 15.f * (columns / 2.f - h) - gWreck_render_area->origin_x,
-gWreck_render_area->origin_y,
gWreck_render_area->width / 2.f - 15.f * (columns / 2.f - h) - gWreck_render_area->origin_x,
gWreck_render_area->height - gWreck_render_area->origin_y,
8);
}
BrZbSceneRenderBegin(gUniverse_actor, gWreck_camera, gWreck_render_area, gWreck_z_buffer);
BrZbSceneRenderAdd(gWreck_root);
BrZbSceneRenderEnd();
if (sel_actor != NULL) {
BrActorRemove(sel_actor);
sel_actor->model = NULL;
BrActorFree(sel_actor);
}
BrPixelmapRectangleFill(gBack_screen,
gCurrent_graf_data->wreck_name_left,
gCurrent_graf_data->wreck_name_top,
gCurrent_graf_data->wreck_name_right - gCurrent_graf_data->wreck_name_left,
gCurrent_graf_data->wreck_name_bottom - gCurrent_graf_data->wreck_name_top,
0);
if (gWreck_selected >= 0 && (gWreck_zoomed_in >= 0 || pCurrent_mode == 0)) {
name = GetDriverName(gWreck_array[gWreck_selected].car_type,
gWreck_array[gWreck_selected].car_index);
TransBrPixelmapText(gBack_screen,
(gCurrent_graf_data->wreck_name_left + gCurrent_graf_data->wreck_name_right - BrPixelmapTextWidth(gBack_screen, gFont_7, name)) / 2,
gCurrent_graf_data->wreck_name_base_line,
84,
gFont_7,
name);
}
#ifdef DETHRACE_3DFX_PATCH
} else {
rows = gCurrent_graf_data->wreck_render_h * 0.0666666f;
columns = (double)gCurrent_graf_data->wreck_render_w * 0.0666666f;
BrPixelmapRectangleFill(gBack_screen, gCurrent_graf_data->wreck_render_x, gCurrent_graf_data->wreck_render_y, gCurrent_graf_data->wreck_render_w, gCurrent_graf_data->wreck_render_h, 0xB0B0B0B0);
for (v = 0; v <= rows; v++) {
BrPixelmapLine(
gBack_screen,
gCurrent_graf_data->wreck_render_x,
gCurrent_graf_data->wreck_render_h / 2.0f + gCurrent_graf_data->wreck_render_y - (rows / 2.0f - v) * 15.0f,
gCurrent_graf_data->wreck_render_w + gCurrent_graf_data->wreck_render_x,
gCurrent_graf_data->wreck_render_h / 2.0f + gCurrent_graf_data->wreck_render_y - (rows / 2.0f - v) * 15.0f,
8);
}
for (h = 0; h <= columns; h++) {
BrPixelmapLine(gBack_screen,
gCurrent_graf_data->wreck_render_w / 2.0f + gCurrent_graf_data->wreck_render_x - (columns / 2.0f - h) * 15.0,
gCurrent_graf_data->wreck_render_y,
gCurrent_graf_data->wreck_render_w / 2.0f + gCurrent_graf_data->wreck_render_x - (columns / 2.0f - h) * 15.0,
gCurrent_graf_data->wreck_render_h + gCurrent_graf_data->wreck_render_y,
8);
}
if (sel_actor) {
BrActorRemove(sel_actor);
sel_actor->model = NULL;
BrActorFree(sel_actor);
}
BrPixelmapRectangleFill(
gBack_screen,
gCurrent_graf_data->wreck_name_left,
gCurrent_graf_data->wreck_name_top,
gCurrent_graf_data->wreck_name_right - gCurrent_graf_data->wreck_name_left,
gCurrent_graf_data->wreck_name_bottom - gCurrent_graf_data->wreck_name_top,
0);
if (gWreck_selected >= 0 && (gWreck_zoomed_in >= 0 || pCurrent_mode == 0)) {
name = GetDriverName(gWreck_array[gWreck_selected].car_type, gWreck_array[gWreck_selected].car_index);
TransBrPixelmapText(gBack_screen,
(gCurrent_graf_data->wreck_name_left + gCurrent_graf_data->wreck_name_right - BrPixelmapTextWidth(gBack_screen, gFont_7, name)) / 2,
gCurrent_graf_data->wreck_name_base_line,
84,
gFont_7,
name);
}
CopyBackScreen(0);
BrPixelmapFill(gWreck_z_buffer, 0xFFFFFFFF);
PDUnlockRealBackScreen(1);
// Added by dethrace
// 3d scene is drawn on top of the 2d hud, so we must ensure that all the 2d pixel
// writes have been flushed to the framebuffer first
BrPixelmapFlush(gReal_back_screen);
// -
BrZbSceneRenderBegin(gUniverse_actor, gWreck_camera, gWreck_render_area, gWreck_z_buffer);
BrZbSceneRenderAdd(gWreck_root);
BrZbSceneRenderEnd();
PDLockRealBackScreen(1);
}
#endif
}
// IDA: int __usercall DamageScrnLeft@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
@ -1222,10 +1313,10 @@ void NetSumDraw(int pCurrent_choice, int pCurrent_mode) {
DRPixelmapRectangleMaskedCopy(gBack_screen,
gCurrent_graf_data->net_sum_x_1,
gCurrent_graf_data->net_sum_headings_y + 1 + i * gCurrent_graf_data->net_sum_y_pitch,
gIcons_pix_low_res, /* DOS version uses low res, Windows version uses normal res */
gIcons_pix_low_res, /* DOS version uses low res, Windows version uses normal res */
0,
gCurrent_graf_data->net_head_icon_height * player->car_index,
gIcons_pix_low_res->width, /* DOS version uses low res, Windows version uses normal res */
gIcons_pix_low_res->width, /* DOS version uses low res, Windows version uses normal res */
gCurrent_graf_data->net_head_icon_height);
TurnOnPaletteConversion();
DrawAnItem__racesumm(gCurrent_graf_data->net_sum_x_2, i, 83, s);

View File

@ -6,6 +6,7 @@
#include "loading.h"
#include "oil.h"
#include "piping.h"
#include "utility.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
@ -139,12 +140,24 @@ void InitSkids(void) {
BrMapAdd(LoadPixelmap(str));
strcpy(str + sl, ".MAT");
gMaterial[mat] = LoadMaterial(str);
if (gMaterial[mat]) {
BrMaterialAdd(gMaterial[mat]);
} else {
if (gMaterial[mat] == NULL) {
BrFatal("..\\..\\source\\common\\skidmark.c", 207, "Couldn't find %s", gMaterial_names[mat]);
}
#ifdef DETHRACE_3DFX_PATCH
GlorifyMaterial(&gMaterial[mat], 1);
#endif
BrMaterialAdd(gMaterial[mat]);
}
#ifdef DETHRACE_3DFX_PATCH
else {
BrMapRemove(gMaterial[mat]->colour_map);
gMaterial[mat]->colour_map = PurifiedPixelmap(gMaterial[mat]->colour_map);
BrMapAdd(gMaterial[mat]->colour_map);
GlorifyMaterial(&gMaterial[mat], 1);
BrMaterialUpdate(gMaterial[mat], BR_MATU_ALL);
}
#endif
}
for (skid = 0; skid < COUNT_OF(gSkids); skid++) {

View File

@ -7,7 +7,9 @@
#include "errors.h"
#include "formats.h"
#include "globvars.h"
#include "globvrbm.h"
#include "globvrkm.h"
#include "globvrpb.h"
#include "graphics.h"
#include "harness/hooks.h"
#include "harness/trace.h"
@ -93,7 +95,22 @@ void SetWorldToScreen(br_pixelmap* pScreen) {
// IDA: void __usercall DrawLine3DThroughBRender(br_vector3 *pStart@<EAX>, br_vector3 *pEnd@<EDX>)
void DrawLine3DThroughBRender(br_vector3* pStart, br_vector3* pEnd) {
LOG_TRACE("(%p, %p)", pStart, pEnd);
NOT_IMPLEMENTED();
gLine_model->vertices[0].p = *pStart;
gLine_model->vertices[1].p = *pEnd;
// HACK: third vertex added by dethrace to work around BR_RSTYLE_EDGES (see `InitLineStuff`)
gLine_model->vertices[2].p = *pEnd;
gLine_model->vertices[2].p.v[0] += 0.001f;
gLine_model->vertices[2].p.v[1] += 0.001f;
gLine_model->vertices[2].p.v[2] += 0.001f;
gLine_model->vertices[2].red = gLine_model->vertices[1].red;
gLine_model->vertices[2].grn = gLine_model->vertices[1].grn;
gLine_model->vertices[2].blu = gLine_model->vertices[1].blu;
// HACK end
BrModelUpdate(gLine_model, BR_MODU_VERTEX_POSITIONS);
BrZbSceneRenderAdd(gLine_actor);
}
// IDA: int __usercall DrawLine3D@<EAX>(br_vector3 *start@<EAX>, br_vector3 *end@<EDX>, br_pixelmap *pScreen@<EBX>, br_pixelmap *pDepth_buffer@<ECX>, br_pixelmap *shade_table)
@ -106,6 +123,13 @@ int DrawLine3D(br_vector3* start, br_vector3* end, br_pixelmap* pScreen, br_pixe
br_scalar ts;
LOG_TRACE("(%p, %p, %p, %p, %p)", start, end, pScreen, pDepth_buffer, shade_table);
#ifdef DETHRACE_3DFX_PATCH
if (gNo_2d_effects) {
DrawLine3DThroughBRender(start, end);
return 999;
}
#endif
o = *start;
p = *end;
if (-gSpark_cam->hither_z < o.v[2] || -gSpark_cam->hither_z < p.v[2]) {
@ -304,7 +328,23 @@ int DrawLine2D(br_vector3* o, br_vector3* p, br_pixelmap* pScreen, br_pixelmap*
// IDA: void __usercall SetLineModelCols(tU8 pCol@<EAX>)
void SetLineModelCols(tU8 pCol) {
LOG_TRACE("(%d)", pCol);
NOT_IMPLEMENTED();
if (pCol != 0) {
gLine_model->vertices[0].red = 255;
gLine_model->vertices[0].grn = 255;
gLine_model->vertices[0].blu = 255;
gLine_model->vertices[1].red = 255;
gLine_model->vertices[1].grn = 255;
gLine_model->vertices[1].blu = 255;
} else {
gLine_model->vertices[0].red = 255;
gLine_model->vertices[0].grn = 0;
gLine_model->vertices[0].blu = 0;
gLine_model->vertices[1].red = 255;
gLine_model->vertices[1].grn = 255;
gLine_model->vertices[1].blu = 0;
}
BrModelUpdate(gLine_model, BR_MODU_ALL);
}
// IDA: void __usercall ReplaySparks(br_pixelmap *pRender_screen@<EAX>, br_pixelmap *pDepth_buffer@<EDX>, br_actor *pCamera@<EBX>, tU32 pTime@<ECX>)
@ -359,8 +399,21 @@ void RenderSparks(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_ac
return;
}
#ifdef DETHRACE_3DFX_PATCH
if (gNo_2d_effects) {
BrActorRemove(gLine_actor);
BrActorAdd(pCamera, gLine_actor);
}
#endif
if (gAction_replay_mode) {
ReplaySparks(pRender_screen, pDepth_buffer, pCamera, pTime);
#ifdef DETHRACE_3DFX_PATCH
if (gNo_2d_effects) {
BrActorRemove(gLine_actor);
BrActorAdd(gDont_render_actor, gLine_actor);
}
#endif
return;
}
StartPipingSession(ePipe_chunk_spark);
@ -419,6 +472,11 @@ void RenderSparks(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_ac
ts = 0.1f;
}
BrVector3Scale(&gSparks[i].v, &gSparks[i].v, ts);
#ifdef DETHRACE_3DFX_PATCH
if (gNo_2d_effects) {
SetLineModelCols(gSparks[i].colour);
}
#endif
if (gSparks[i].colour) {
DrawLine3D(&p, &new_pos, pRender_screen, pDepth_buffer, gFog_shade_table);
} else {
@ -426,6 +484,12 @@ void RenderSparks(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_ac
}
}
EndPipingSession();
#ifdef DETHRACE_3DFX_PATCH
if (gNo_2d_effects) {
BrActorRemove(gLine_actor);
BrActorAdd(gDont_render_actor, gLine_actor);
}
#endif
}
// IDA: void __usercall CreateSingleSpark(tCar_spec *pCar@<EAX>, br_vector3 *pPos@<EDX>, br_vector3 *pVel@<EBX>)
@ -1074,11 +1138,20 @@ void SmokeCircle(br_vector3* o, br_scalar r, br_scalar extra_z, br_scalar streng
}
// IDA: int __cdecl CmpSmokeZ(void *p1, void *p2)
int CmpSmokeZ(void* p1, void* p2) {
int CmpSmokeZ(const void* p1, const void* p2) {
tBRender_smoke** a;
tBRender_smoke** b;
LOG_TRACE("(%p, %p)", p1, p2);
NOT_IMPLEMENTED();
a = (tBRender_smoke**)p1;
b = (tBRender_smoke**)p2;
if ((*a)->pos.v[2] == (*b)->pos.v[2]) {
return 0;
} else if ((*a)->pos.v[2] > (*b)->pos.v[2]) {
return 1;
} else {
return -1;
}
}
// IDA: void __cdecl RenderRecordedSmokeCircles()
@ -1089,7 +1162,35 @@ void RenderRecordedSmokeCircles(void) {
tU8 grn;
tU8 blu;
LOG_TRACE("()");
NOT_IMPLEMENTED();
BrQsort(gBR_smoke_pointers, gN_BR_smoke_structs, sizeof(void*), CmpSmokeZ);
for (i = 0; i < gN_BR_smoke_structs; i++) {
smoke = gBR_smoke_pointers[i];
BrVector3Copy(&gBlend_actor->t.t.translate.t, &smoke->pos);
gBlend_actor->t.t.mat.m[0][0] = smoke->r;
gBlend_actor->t.t.mat.m[1][1] = smoke->r / smoke->aspect;
gBlend_material->extra_prim[1].v.x = BrFloatToFixed(smoke->strength * 150.0f);
BrMaterialUpdate(gBlend_material, BR_MATU_EXTRA_PRIM);
red = BR_RED(smoke->col);
grn = BR_GRN(smoke->col);
blu = BR_BLU(smoke->col);
gBlend_model->vertices[0].red = red;
gBlend_model->vertices[0].grn = grn;
gBlend_model->vertices[0].blu = blu;
gBlend_model->vertices[1].red = red;
gBlend_model->vertices[1].grn = grn;
gBlend_model->vertices[1].blu = blu;
gBlend_model->vertices[2].red = red;
gBlend_model->vertices[2].grn = grn;
gBlend_model->vertices[2].blu = blu;
gBlend_model->vertices[3].red = red;
gBlend_model->vertices[3].grn = grn;
gBlend_model->vertices[3].blu = blu;
BrModelUpdate(gBlend_model, BR_MODU_VERTEX_COLOURS);
BrZbSceneRenderAdd(gBlend_actor);
}
}
// IDA: void __usercall RecordSmokeCircle(br_vector3 *pCent@<EAX>, br_scalar pR, br_scalar pStrength, br_pixelmap *pShade, br_scalar pAspect)
@ -1097,7 +1198,21 @@ void RecordSmokeCircle(br_vector3* pCent, br_scalar pR, br_scalar pStrength, br_
tU8 shade_index;
br_colour shade_rgb;
LOG_TRACE("(%p, %f, %f, %p, %f)", pCent, pR, pStrength, pShade, pAspect);
NOT_IMPLEMENTED();
if (gRendering_mirror) {
DRMatrix34TApplyP(&gBR_smoke_structs[gN_BR_smoke_structs].pos, pCent, &gRearview_camera_to_world);
} else {
DRMatrix34TApplyP(&gBR_smoke_structs[gN_BR_smoke_structs].pos, pCent, &gCamera_to_world);
}
gBR_smoke_structs[gN_BR_smoke_structs].r = pR;
gBR_smoke_structs[gN_BR_smoke_structs].strength = pStrength;
shade_index = ((tU8*)pShade->pixels)[pShade->row_bytes * (pShade->height - 1)];
shade_rgb = ((br_colour*)gRender_palette->pixels)[shade_index];
gBR_smoke_structs[gN_BR_smoke_structs].col = shade_rgb;
gBR_smoke_structs[gN_BR_smoke_structs].aspect = pAspect;
gBR_smoke_pointers[gN_BR_smoke_structs] = &gBR_smoke_structs[gN_BR_smoke_structs];
gN_BR_smoke_structs++;
}
// IDA: void __usercall SmokeCircle3D(br_vector3 *o@<EAX>, br_scalar r, br_scalar strength, br_scalar pAspect, br_pixelmap *pRender_screen, br_pixelmap *pDepth_buffer, br_pixelmap *pShade_table, br_actor *pCam)
@ -1111,6 +1226,12 @@ void SmokeCircle3D(br_vector3* o, br_scalar r, br_scalar strength, br_scalar pAs
LOG_TRACE("(%p, %f, %f, %f, %p, %p, %p, %p)", o, r, strength, pAspect, pRender_screen, pDepth_buffer, pShade_table, pCam);
cam = pCam->type_data;
if (gNo_2d_effects) {
RecordSmokeCircle(o, r, strength, pShade_table, pAspect);
return;
}
srand(o->v[2] * 16777216.0f + o->v[1] * 65536.0f + o->v[0] * 256.0f + r);
BrVector3Sub(&tv, o, (br_vector3*)gCamera_to_world.m[3]);
BrMatrix34TApplyV(&p, &tv, &gCamera_to_world);
@ -1229,85 +1350,115 @@ void RenderSmoke(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_act
LOG_TRACE("(%p, %p, %p, %p, %d)", pRender_screen, pDepth_buffer, pCamera, pCamera_to_world, pTime);
not_lonely = 0;
#ifdef DETHRACE_3DFX_PATCH
if (gNo_2d_effects) {
gBlend_actor->render_style = BR_RSTYLE_FACES;
BrActorRemove(gBlend_actor);
BrActorAdd(pCamera, gBlend_actor);
gN_BR_smoke_structs = 0;
}
#endif
DrawTheGlow(pRender_screen, pDepth_buffer, pCamera);
if (gSmoke_flags != 0) {
seed = rand();
if (gAction_replay_mode) {
ReplaySmoke(pRender_screen, pDepth_buffer, pCamera);
srand(seed);
} else {
StartPipingSession(ePipe_chunk_smoke);
for (i = 0; i < COUNT_OF(gSmoke); i++) {
if ((gSmoke_flags & (1u << i)) != 0) {
if (gSmoke[i].strength > 0.0) {
if (gSmoke[i].time_sync) {
BrVector3Scale(&tv, &gSmoke[i].v, gSmoke[i].time_sync / 1000.0);
gSmoke[i].time_sync = 0;
} else {
BrVector3Scale(&tv, &gSmoke[i].v, pTime / 1000.0);
}
BrVector3Accumulate(&gSmoke[i].pos, &tv);
} else {
gSmoke_flags &= ~(1u << i);
}
if (gSmoke_flags == 0) {
#ifdef DETHRACE_3DFX_PATCH
if (gNo_2d_effects) {
BrActorRemove(gBlend_actor);
BrActorAdd(gDont_render_actor, gBlend_actor);
}
#endif
return;
}
seed = rand();
if (gAction_replay_mode) {
ReplaySmoke(pRender_screen, pDepth_buffer, pCamera);
srand(seed);
#ifdef DETHRACE_3DFX_PATCH
if (gNo_2d_effects) {
RenderRecordedSmokeCircles();
BrActorRemove(gBlend_actor);
BrActorAdd(gDont_render_actor, gBlend_actor);
}
#endif
return;
}
StartPipingSession(ePipe_chunk_smoke);
for (i = 0; i < COUNT_OF(gSmoke); i++) {
if ((gSmoke_flags & (1u << i)) != 0) {
if (gSmoke[i].strength > 0.0) {
if (gSmoke[i].time_sync) {
BrVector3Scale(&tv, &gSmoke[i].v, gSmoke[i].time_sync / 1000.0);
gSmoke[i].time_sync = 0;
} else {
BrVector3Scale(&tv, &gSmoke[i].v, pTime / 1000.0);
}
BrVector3Accumulate(&gSmoke[i].pos, &tv);
} else {
gSmoke_flags &= ~(1u << i);
}
for (i = 0; i < COUNT_OF(gSmoke); i++) {
if ((gSmoke_flags & (1u << i)) != 0) {
if ((gSmoke[i].type & 0xf) == 7) {
not_lonely |= 1u << i;
} else if ((not_lonely & (1u << i)) == 0) {
for (j = i + 1; j < COUNT_OF(gSmoke); j++) {
if ((gSmoke_flags & (1u << j)) != 0) {
BrVector3Sub(&tv, &gSmoke[i].pos, &gSmoke[i].pos);
ts = BrVector3LengthSquared(&tv);
if ((gSmoke[i].radius + gSmoke[j].radius) * (gSmoke[i].radius + gSmoke[j].radius) > ts) {
not_lonely |= (1u << j) | (1u << i);
break;
}
}
}
}
if (((1u << i) & not_lonely) == 0) {
gSmoke[i].strength = gSmoke[i].strength / 2.0;
}
aspect = (gSmoke[i].radius - 0.05f) / 0.25f * 0.5f + 1.0f;
if ((gSmoke[i].type & 0x10) != 0) {
SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius / aspect, gSmoke[i].strength, 1.0, pRender_screen, pDepth_buffer, gShade_list[gSmoke[i].type & 0xf], pCamera);
} else {
SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius, gSmoke[i].strength, aspect, pRender_screen, pDepth_buffer, gShade_list[gSmoke[i].type & 0xf], pCamera);
}
if (gSmoke[i].pipe_me) {
AddSmokeToPipingSession(i, gSmoke[i].type, &gSmoke[i].pos, gSmoke[i].radius, gSmoke[i].strength);
}
gSmoke[i].radius = (double)pTime / 1000.0 * gSmoke[i].strength * 0.5 + gSmoke[i].radius;
gSmoke[i].strength = gSmoke[i].strength - (double)pTime * gSmoke[i].decay_factor / 1000.0;
if (gSmoke[i].radius > 0.3f) {
gSmoke[i].radius = 0.3f;
}
if (gSmoke[i].strength > 0.0) {
ts = 1.0f - (double)pTime * 0.002f;
if (ts < 0.5f) {
ts = 0.5f;
}
BrVector3Scale(&gSmoke[i].v, &gSmoke[i].v, ts);
if (fabs(gSmoke[i].v.v[1]) < 0.43478259f && (gSmoke[i].type & 0xFu) < 7) {
if (gSmoke[i].v.v[1] >= 0.0) {
gSmoke[i].v.v[1] = 0.43478259f;
} else {
gSmoke[i].v.v[1] += 0.43478259f;
}
}
} else {
gSmoke_flags &= ~(1u << i);
}
}
}
EndPipingSession();
srand(seed);
}
}
for (i = 0; i < COUNT_OF(gSmoke); i++) {
if ((gSmoke_flags & (1u << i)) != 0) {
if ((gSmoke[i].type & 0xf) == 7) {
not_lonely |= 1u << i;
} else if ((not_lonely & (1u << i)) == 0) {
for (j = i + 1; j < COUNT_OF(gSmoke); j++) {
if ((gSmoke_flags & (1u << j)) != 0) {
BrVector3Sub(&tv, &gSmoke[i].pos, &gSmoke[i].pos);
ts = BrVector3LengthSquared(&tv);
if ((gSmoke[i].radius + gSmoke[j].radius) * (gSmoke[i].radius + gSmoke[j].radius) > ts) {
not_lonely |= (1u << j) | (1u << i);
break;
}
}
}
}
if (((1u << i) & not_lonely) == 0) {
gSmoke[i].strength = gSmoke[i].strength / 2.0;
}
aspect = (gSmoke[i].radius - 0.05f) / 0.25f * 0.5f + 1.0f;
if ((gSmoke[i].type & 0x10) != 0) {
SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius / aspect, gSmoke[i].strength, 1.0, pRender_screen, pDepth_buffer, gShade_list[gSmoke[i].type & 0xf], pCamera);
} else {
SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius, gSmoke[i].strength, aspect, pRender_screen, pDepth_buffer, gShade_list[gSmoke[i].type & 0xf], pCamera);
}
if (gSmoke[i].pipe_me) {
AddSmokeToPipingSession(i, gSmoke[i].type, &gSmoke[i].pos, gSmoke[i].radius, gSmoke[i].strength);
}
gSmoke[i].radius = (double)pTime / 1000.0 * gSmoke[i].strength * 0.5 + gSmoke[i].radius;
gSmoke[i].strength = gSmoke[i].strength - (double)pTime * gSmoke[i].decay_factor / 1000.0;
if (gSmoke[i].radius > 0.3f) {
gSmoke[i].radius = 0.3f;
}
if (gSmoke[i].strength > 0.0) {
ts = 1.0f - (double)pTime * 0.002f;
if (ts < 0.5f) {
ts = 0.5f;
}
BrVector3Scale(&gSmoke[i].v, &gSmoke[i].v, ts);
if (fabs(gSmoke[i].v.v[1]) < 0.43478259f && (gSmoke[i].type & 0xFu) < 7) {
if (gSmoke[i].v.v[1] >= 0.0) {
gSmoke[i].v.v[1] = 0.43478259f;
} else {
gSmoke[i].v.v[1] += 0.43478259f;
}
}
} else {
gSmoke_flags &= ~(1u << i);
}
}
}
EndPipingSession();
srand(seed);
#ifdef DETHRACE_3DFX_PATCH
if (gNo_2d_effects) {
RenderRecordedSmokeCircles();
BrActorRemove(gBlend_actor);
BrActorAdd(gDont_render_actor, gBlend_actor);
}
#endif
}
// IDA: void __usercall CreatePuffOfSmoke(br_vector3 *pos@<EAX>, br_vector3 *v@<EDX>, br_scalar strength, br_scalar pDecay_factor, int pType, tCar_spec *pC)

View File

@ -92,7 +92,7 @@ void SmokeLine(int l, int x, br_scalar zbuff, int r_squared, tU8* scr_ptr, tU16*
void SmokeCircle(br_vector3* o, br_scalar r, br_scalar extra_z, br_scalar strength, br_scalar pAspect, br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_pixelmap* pShade_table);
int CmpSmokeZ(void* p1, void* p2);
int CmpSmokeZ(const void* p1, const void* p2);
void RenderRecordedSmokeCircles(void);

View File

@ -659,7 +659,9 @@ void InitialiseProgramState(void) {
gProgram_state.cockpit_on = gCockpit_on;
gProgram_state.car_name[0] = 0;
SetSoundVolumes();
#if !defined(DETHRACE_3DFX_PATCH)
AllocateRearviewPixelmap();
#endif
}
// IDA: void __cdecl DoProgram()

View File

@ -5,6 +5,7 @@
#include "constants.h"
#include "errors.h"
#include "globvars.h"
#include "globvrbm.h"
#include "globvrpb.h"
#include "graphics.h"
#include "harness/config.h"
@ -416,7 +417,27 @@ void CopyDoubled8BitTo16BitRectangle(br_pixelmap* pDst, br_pixelmap* pSrc, int p
tU16* dst_start1;
tU16* palette_entry;
LOG_TRACE("(%p, %p, %d, %d, %d, %d, %p)", pDst, pSrc, pSrc_width, pSrc_height, pDst_x, pDst_y, pPalette);
NOT_IMPLEMENTED();
palette_entry = PaletteOf16Bits(pPalette)->pixels;
for (y = 0; y < pSrc_height; y++) {
src_start = ((tU8*)pSrc->pixels) + pSrc->row_bytes * y;
dst_start0 = (tU16*)(((tU8*)pDst->pixels) + pDst->row_bytes * pDst_y);
dst_start0 += pDst_x;
dst_start1 = (tU16*)(((tU8*)pDst->pixels) + pDst->row_bytes * (pDst_y + 1));
dst_start1 += pDst_x;
for (x = 0; x < pSrc_width; x++) {
dst_start0[0] = palette_entry[*src_start];
dst_start0[1] = palette_entry[*src_start];
dst_start1[0] = palette_entry[*src_start];
dst_start1[1] = palette_entry[*src_start];
src_start++;
dst_start0 += 2;
dst_start1 += 2;
}
pDst_y += 2;
}
}
// IDA: br_pixelmap* __usercall Scale8BitPixelmap@<EAX>(br_pixelmap *pSrc@<EAX>, int pWidth@<EDX>, int pHeight@<EBX>)
@ -447,7 +468,14 @@ br_pixelmap* Tile8BitPixelmap(br_pixelmap* pSrc, int pN) {
// IDA: tException_list __usercall FindExceptionInList@<EAX>(char *pName@<EAX>, tException_list pList@<EDX>)
tException_list FindExceptionInList(char* pName, tException_list pList) {
LOG_TRACE("(\"%s\", %d)", pName, pList);
NOT_IMPLEMENTED();
while (pList) {
if (DRStricmp(pName, pList->name) == 0) {
return pList;
}
pList = pList->next;
}
return NULL;
}
// IDA: br_pixelmap* __usercall PurifiedPixelmap@<EAX>(br_pixelmap *pSrc@<EAX>)
@ -458,7 +486,14 @@ br_pixelmap* PurifiedPixelmap(br_pixelmap* pSrc) {
int new_height;
tException_list e;
LOG_TRACE("(%p)", pSrc);
NOT_IMPLEMENTED();
// dethrace: added conditional to allow both software and 3dfx modes
if (!harness_game_config.opengl_3dfx_mode) {
return pSrc;
}
LOG_INFO("PurifiedPixelmap not implemented");
return pSrc;
}
// IDA: br_pixelmap* __usercall DRPixelmapLoad@<EAX>(char *pFile_name@<EAX>)
@ -721,10 +756,18 @@ void PrintScreen(void) {
LOG_TRACE("()");
f = OpenUniqueFileB("DUMP", "BMP");
if (f != NULL) {
PrintScreenFile(f);
fclose(f);
if (f == NULL) {
return;
}
#ifdef DETHRACE_3DFX_PATCH
if (gBack_screen->type == BR_PMT_RGB_565) {
PrintScreenFile16(f);
} else
#endif
{
PrintScreenFile(f);
}
fclose(f);
}
// IDA: tU32 __cdecl GetTotalTime()
@ -1006,7 +1049,9 @@ tU16 PaletteEntry16Bit(br_pixelmap* pPal, int pEntry) {
int green;
int blue;
LOG_TRACE("(%p, %d)", pPal, pEntry);
NOT_IMPLEMENTED();
src_entry = pPal->pixels;
return ((tU8)src_entry[pEntry] >> 3) | (((src_entry[pEntry] >> 19) & 0x1F) << 11) | (32 * ((tU16)src_entry[pEntry] >> 10));
}
// IDA: br_pixelmap* __usercall PaletteOf16Bits@<EAX>(br_pixelmap *pSrc@<EAX>)
@ -1014,7 +1059,24 @@ br_pixelmap* PaletteOf16Bits(br_pixelmap* pSrc) {
tU16* dst_entry;
int value;
LOG_TRACE("(%p)", pSrc);
NOT_IMPLEMENTED();
if (g16bit_palette == NULL) {
g16bit_palette = BrPixelmapAllocate(BR_PMT_RGB_565, 1, 256, g16bit_palette, 0);
if (g16bit_palette == NULL) {
FatalError(94, "16-bit palette");
}
}
if (!g16bit_palette_valid || pSrc != gSource_for_16bit_palette) {
value = 0;
dst_entry = g16bit_palette->pixels;
for (value = 0; value < 256; value++) {
*dst_entry = PaletteEntry16Bit(pSrc, value);
dst_entry++;
}
gSource_for_16bit_palette = pSrc;
g16bit_palette_valid = 1;
}
return g16bit_palette;
}
// IDA: void __usercall Copy8BitTo16Bit(br_pixelmap *pDst@<EAX>, br_pixelmap *pSrc@<EDX>, br_pixelmap *pPalette@<EBX>)
@ -1025,7 +1087,17 @@ void Copy8BitTo16Bit(br_pixelmap* pDst, br_pixelmap* pSrc, br_pixelmap* pPalette
tU16* dst_start;
tU16* palette_entry;
LOG_TRACE("(%p, %p, %p)", pDst, pSrc, pPalette);
NOT_IMPLEMENTED();
palette_entry = PaletteOf16Bits(pPalette)->pixels;
for (y = 0; y < pDst->height; y++) {
src_start = (tU8*)pSrc->pixels + pSrc->row_bytes * y;
dst_start = (tU16*)((tU8*)pDst->pixels + pDst->row_bytes * y);
for (x = 0; x < pDst->width; x++) {
*dst_start = palette_entry[*src_start];
src_start++;
dst_start++;
}
}
}
// IDA: void __usercall Copy8BitTo16BitRectangle(br_pixelmap *pDst@<EAX>, tS16 pDst_x@<EDX>, tS16 pDst_y@<EBX>, br_pixelmap *pSrc@<ECX>, tS16 pSrc_x, tS16 pSrc_y, tS16 pWidth, tS16 pHeight, br_pixelmap *pPalette)
@ -1036,7 +1108,57 @@ void Copy8BitTo16BitRectangle(br_pixelmap* pDst, tS16 pDst_x, tS16 pDst_y, br_pi
tU16* dst_start;
tU16* palette_entry;
LOG_TRACE("(%p, %d, %d, %p, %d, %d, %d, %d, %p)", pDst, pDst_x, pDst_y, pSrc, pSrc_x, pSrc_y, pWidth, pHeight, pPalette);
NOT_IMPLEMENTED();
if (pSrc_x < 0) {
pWidth = pSrc_x + pWidth;
pDst_x = pDst_x - pSrc_x;
pSrc_x = 0;
}
if (pDst_x < 0) {
pWidth += pDst_x;
pSrc_x -= pDst_x;
pDst_x = 0;
}
if (pSrc_y < 0) {
pHeight = pSrc_y + pHeight;
pDst_y = pDst_y - pSrc_y;
pSrc_y = 0;
}
if (pDst_y < 0) {
pHeight += pDst_y;
pSrc_y -= pDst_y;
pDst_y = 0;
}
if (pSrc_x + pWidth > pSrc->width) {
pWidth = pSrc->width - pSrc_x;
}
if (pSrc_y + pHeight > pSrc->height) {
pHeight = pSrc->height - pSrc_y;
}
if (pDst_x + pWidth > pDst->width) {
pWidth = pDst->width - pDst_x;
}
if (pDst_y + pHeight > pDst->height) {
pHeight = pDst->height - pDst_y;
}
palette_entry = PaletteOf16Bits(pPalette)->pixels;
for (y = 0; y < pHeight; y++) {
src_start = (tU8*)pSrc->pixels + (pSrc->row_bytes * (pSrc_y + y));
src_start += pSrc_x;
dst_start = (tU16*)((tU8*)pDst->pixels + (pDst->row_bytes * (pDst_y + y)));
dst_start += pDst_x;
for (x = 0; x < pWidth; x++) {
// even though we have a specific `WithTransparency` version of this function, this one also handles transparency!
if (*src_start != 0) {
*dst_start = palette_entry[*src_start];
}
src_start++;
dst_start++;
}
}
}
// IDA: void __usercall Copy8BitTo16BitRectangleWithTransparency(br_pixelmap *pDst@<EAX>, tS16 pDst_x@<EDX>, tS16 pDst_y@<EBX>, br_pixelmap *pSrc@<ECX>, tS16 pSrc_x, tS16 pSrc_y, tS16 pWidth, tS16 pHeight, br_pixelmap *pPalette)
@ -1047,7 +1169,55 @@ void Copy8BitTo16BitRectangleWithTransparency(br_pixelmap* pDst, tS16 pDst_x, tS
tU16* dst_start;
tU16* palette_entry;
LOG_TRACE("(%p, %d, %d, %p, %d, %d, %d, %d, %p)", pDst, pDst_x, pDst_y, pSrc, pSrc_x, pSrc_y, pWidth, pHeight, pPalette);
NOT_IMPLEMENTED();
if (pSrc_x < 0) {
pWidth = pSrc_x + pWidth;
pDst_x = pDst_x - pSrc_x;
pSrc_x = 0;
}
if (pDst_x < 0) {
pWidth += pDst_x;
pSrc_x -= pDst_x;
pDst_x = 0;
}
if (pSrc_y < 0) {
pHeight = pSrc_y + pHeight;
pDst_y = pDst_y - pSrc_y;
pSrc_y = 0;
}
if (pDst_y < 0) {
pHeight += pDst_y;
pSrc_y -= pDst_y;
pDst_y = 0;
}
if (pSrc_x + pWidth > pSrc->width) {
pWidth = pSrc->width - pSrc_x;
}
if (pSrc_y + pHeight > pSrc->height) {
pHeight = pSrc->height - pSrc_y;
}
if (pDst_x + pWidth > pDst->width) {
pWidth = pDst->width - pDst_x;
}
if (pDst_y + pHeight > pDst->height) {
pHeight = pDst->height - pDst_y;
}
palette_entry = PaletteOf16Bits(pPalette)->pixels;
for (y = 0; y < pHeight; y++) {
src_start = (tU8*)pSrc->pixels + (pSrc->row_bytes * (pSrc_y + y)) + pSrc_x;
dst_start = (tU16*)((tU8*)pDst->pixels + (pDst->row_bytes * (pDst_y + y)));
dst_start += pDst_x;
for (x = 0; x < pWidth; x++) {
if (*src_start != 0) {
*dst_start = palette_entry[*src_start];
}
src_start++;
dst_start++;
}
}
}
// IDA: void __usercall Copy8BitToOnscreen16BitRectangleWithTransparency(br_pixelmap *pDst@<EAX>, tS16 pDst_x@<EDX>, tS16 pDst_y@<EBX>, br_pixelmap *pSrc@<ECX>, tS16 pSrc_x, tS16 pSrc_y, tS16 pWidth, tS16 pHeight, br_pixelmap *pPalette)
@ -1058,7 +1228,20 @@ void Copy8BitToOnscreen16BitRectangleWithTransparency(br_pixelmap* pDst, tS16 pD
tU16* dst_start;
tU16* palette_entry;
LOG_TRACE("(%p, %d, %d, %p, %d, %d, %d, %d, %p)", pDst, pDst_x, pDst_y, pSrc, pSrc_x, pSrc_y, pWidth, pHeight, pPalette);
NOT_IMPLEMENTED();
palette_entry = PaletteOf16Bits(pPalette)->pixels;
for (y = 0; y < pHeight; y++) {
src_start = (tU8*)pSrc->pixels + (pSrc->row_bytes * (pSrc_y + y)) + pSrc_x;
dst_start = (tU16*)((tU8*)pDst->pixels + (pDst->row_bytes * (pDst_y + y)));
dst_start += pDst_x;
for (x = 0; x < pWidth; x++) {
if (*src_start != 0) {
*dst_start = palette_entry[*src_start];
}
src_start++;
dst_start++;
}
}
}
// IDA: void __usercall Copy8BitRectangleTo16BitRhombusWithTransparency(br_pixelmap *pDst@<EAX>, tS16 pDst_x@<EDX>, tS16 pDst_y@<EBX>, br_pixelmap *pSrc@<ECX>, tS16 pSrc_x, tS16 pSrc_y, tS16 pWidth, tS16 pHeight, tX1616 pShear, br_pixelmap *pPalette)
@ -1073,21 +1256,95 @@ void Copy8BitRectangleTo16BitRhombusWithTransparency(br_pixelmap* pDst, tS16 pDs
tS16 clipped_src_x;
tS16 clipped_width;
LOG_TRACE("(%p, %d, %d, %p, %d, %d, %d, %d, %d, %p)", pDst, pDst_x, pDst_y, pSrc, pSrc_x, pSrc_y, pWidth, pHeight, pShear, pPalette);
NOT_IMPLEMENTED();
palette_entry = PaletteOf16Bits(pPalette)->pixels;
total_shear = 0;
if (pSrc_y + pSrc->origin_y < 0) {
pHeight += pSrc_y + pSrc->origin_y;
pDst_y -= (pSrc_y + pSrc->origin_y);
pSrc_y = 0;
}
if (pDst_y + pDst->origin_y < 0) {
pHeight += pDst_y + pDst->origin_y;
pSrc_y -= (pDst_y + pDst->origin_y);
pDst_y = 0;
}
if (pSrc_y + pSrc->origin_y + pHeight > pSrc->height) {
pHeight = pSrc->height - (pSrc_y + pSrc->origin_y);
}
if (pDst_y + pDst->origin_y + pHeight > pDst->height) {
pHeight = pDst->height - pDst_y + pDst->origin_y;
}
if (pHeight > 0) {
if (pSrc_x + pSrc->origin_x < 0) {
pWidth += pSrc_x + pSrc->origin_x;
pDst_x -= (pSrc_x + pSrc->origin_x);
pSrc_x = 0;
}
if (pSrc_x + pSrc->origin_x + pWidth > pSrc->width) {
pWidth = pSrc->width - (pSrc_x + pSrc->origin_x);
}
for (y = 0; y < pHeight; y++) {
clipped_src_x = pSrc_x + pSrc->origin_x;
sheared_x = pDst_x + pDst->origin_x + (total_shear >> 16);
clipped_width = pWidth;
if ((sheared_x & 0x8000u) != 0) {
clipped_width = pWidth + sheared_x;
clipped_src_x = (pSrc_x + pSrc->origin_x) - sheared_x;
sheared_x = 0;
}
if (sheared_x + clipped_width > pDst->width) {
clipped_width = pDst->width - sheared_x;
}
if (clipped_width > 0) {
src_start = ((tU8*)pSrc->pixels) + (y + pSrc_y + pSrc->origin_y) * pSrc->row_bytes + clipped_src_x;
dst_start = (tU16*)((tU8*)pDst->pixels + 2 * sheared_x + (y + pDst_y + pDst->origin_y) * pDst->row_bytes);
for (x = clipped_width; x > 0; x--) {
if (*src_start) {
*dst_start = palette_entry[*src_start];
}
src_start++;
dst_start++;
}
}
total_shear += pShear;
}
}
}
// IDA: void __usercall DRPixelmapRectangleCopy(br_pixelmap *dst@<EAX>, br_int_16 dx@<EDX>, br_int_16 dy@<EBX>, br_pixelmap *src@<ECX>, br_int_16 sx, br_int_16 sy, br_uint_16 w, br_uint_16 h)
void DRPixelmapRectangleCopy(br_pixelmap* dst, br_int_16 dx, br_int_16 dy, br_pixelmap* src, br_int_16 sx, br_int_16 sy, br_uint_16 w, br_uint_16 h) {
LOG_TRACE("(%p, %d, %d, %p, %d, %d, %d, %d)", dst, dx, dy, src, sx, sy, w, h);
#ifdef DETHRACE_3DFX_PATCH
if (dst->type == src->type) {
BrPixelmapRectangleCopy(dst, dx, dy, src, sx, sy, w, h);
} else if (dst->type == BR_PMT_RGB_565 && src->type == BR_PMT_INDEX_8) {
Copy8BitTo16BitRectangle(dst, dx, dy, src, sx, sy, w, h, gCurrent_palette);
}
#else
BrPixelmapRectangleCopy(dst, dx, dy, src, sx, sy, w, h);
#endif
}
// IDA: void __usercall DRPixelmapCopy(br_pixelmap *dst@<EAX>, br_pixelmap *src@<EDX>)
void DRPixelmapCopy(br_pixelmap* dst, br_pixelmap* src) {
LOG_TRACE("(%p, %p)", dst, src);
#ifdef DETHRACE_3DFX_PATCH
if (dst->type == src->type) {
BrPixelmapCopy(dst, src);
} else if (dst->type == BR_PMT_RGB_565 && src->type == BR_PMT_INDEX_8) {
Copy8BitTo16Bit(dst, src, gCurrent_palette);
}
#else
BrPixelmapCopy(dst, src);
#endif
}
// IDA: void __usercall DRPixelmapRectangleFill(br_pixelmap *dst@<EAX>, br_int_16 x@<EDX>, br_int_16 y@<EBX>, br_uint_16 w@<ECX>, br_uint_16 h, br_uint_32 colour)
@ -1163,7 +1420,7 @@ void SubsStringJob(char* pStr, ...) {
va_end(ap);
return;
}
sub_str = va_arg(ap, char *);
sub_str = va_arg(ap, char*);
StripCR(sub_str);
strcpy(temp_str, &sub_pt[1]);
strcpy(sub_pt, sub_str);
@ -1462,7 +1719,50 @@ void GlorifyMaterial(br_material** pArray, int pCount) {
br_pixelmap* big_tile;
tException_list e;
LOG_TRACE("(%p, %d)", pArray, pCount);
NOT_IMPLEMENTED();
// Added by dethrace.
// `GlorifyMaterial` is only present in the 3dfx patch.
// If software mode, don't glorify, otherwise it puts the software renderer into lit mode
// See `WhitenVertexRGB` for a similar check that is present in the original code
if (!harness_game_config.opengl_3dfx_mode) {
return;
}
// <<<
for (i = 0; i < pCount; i++) {
if (pArray[i]->colour_map != NULL) {
e = FindExceptionInList(pArray[i]->colour_map->identifier, gExceptions);
if (gInterpolate_textures) {
// use linear texture filtering unless we have a "nobilinear" flag or the texture has transparent parts
if ((e == NULL || (e->flags & ExceptionFlag_NoBilinear) == 0)
&& !DRPixelmapHasZeros(pArray[i]->colour_map)) {
pArray[i]->flags |= BR_MATF_MAP_INTERPOLATION;
}
}
if (gUse_mip_maps) {
pArray[i]->flags |= BR_MATF_MAP_ANTIALIASING;
}
if (gPerspective_is_fast) {
pArray[i]->flags |= BR_MATF_PERSPECTIVE;
}
if (e && (e->flags & ExceptionFlag_Double)) {
pArray[i]->map_transform.m[0][0] = 0.5f;
pArray[i]->map_transform.m[1][1] = 0.5f;
} else if (e && (e->flags & ExceptionFlag_Quadruple)) {
pArray[i]->map_transform.m[0][0] = 0.25f;
pArray[i]->map_transform.m[1][1] = 0.25f;
}
} else {
c = pArray[i]->index_base + pArray[i]->index_range / 2;
pArray[i]->colour = ((br_colour*)gRender_palette->pixels)[c];
}
pArray[i]->ka = 1.0f;
pArray[i]->kd = 0.0f;
pArray[i]->ks = 0.0f;
pArray[i]->flags &= ~BR_MATF_PRELIT;
pArray[i]->flags |= BR_MATF_LIGHT;
}
}
// IDA: void __usercall WhitenVertexRGB(br_model **pArray@<EAX>, int pN@<EDX>)
@ -1471,7 +1771,17 @@ void WhitenVertexRGB(br_model** pArray, int pN) {
int v;
br_vertex* vertex;
LOG_TRACE("(%p, %d)", pArray, pN);
NOT_IMPLEMENTED();
if (gScreen && gScreen->type != BR_PMT_INDEX_8 && pN > 0) {
for (m = 0; m < pN; m++) {
vertex = pArray[m]->vertices;
for (v = 0; v < pArray[m]->nvertices; v++, vertex++) {
vertex->red = 255;
vertex->grn = 255;
vertex->blu = 255;
}
}
}
}
// IDA: void __usercall NobbleNonzeroBlacks(br_pixelmap *pPalette@<EAX>)
@ -1483,7 +1793,29 @@ void NobbleNonzeroBlacks(br_pixelmap* pPalette) {
tU32* palette_entry;
tU32 frobbed;
LOG_TRACE("(%p)", pPalette);
NOT_IMPLEMENTED();
int i;
palette_entry = pPalette->pixels;
frobbed = 0;
if (*palette_entry != 0) {
*palette_entry = 0;
frobbed = 1;
}
palette_entry++;
for (i = 1; i < 256; i++) {
blue = (*palette_entry >> 16) & 0xff;
green = (*palette_entry >> 8) & 0xff;
red = (*palette_entry) & 0xff;
if (blue == 0 && green == 0 && red == 0) {
frobbed = 1;
*palette_entry = 0x010101;
}
palette_entry++;
}
if (frobbed) {
BrMapUpdate(pPalette, BR_MAPU_ALL);
}
}
// IDA: int __usercall PDCheckDriveExists@<EAX>(char *pThe_path@<EAX>)

View File

@ -12,6 +12,7 @@
#include "flicplay.h"
#include "formats.h"
#include "globvars.h"
#include "globvrbm.h"
#include "globvrpb.h"
#include "graphics.h"
#include "harness/trace.h"
@ -486,6 +487,9 @@ int LoadNMaterials(tBrender_storage* pStorage_space, FILE* pF, int pCount) {
if (total == 0) {
FatalError(kFatalError_LoadMaterialFile_S, str);
}
#ifdef DETHRACE_3DFX_PATCH
GlorifyMaterial(temp_array, total);
#endif
for (j = 0; j < total; j++) {
if (temp_array[j]) {
switch (AddMaterialToStorage(pStorage_space, temp_array[j])) {
@ -527,6 +531,9 @@ int LoadNModels(tBrender_storage* pStorage_space, FILE* pF, int pCount) {
PathCat(the_path, gApplication_path, "MODELS");
PathCat(the_path, the_path, str);
total = BrModelLoadMany(the_path, temp_array, 2000);
#ifdef DETHRACE_3DFX_PATCH
WhitenVertexRGB(temp_array, total);
#endif
if (total == 0) {
FatalError(kFatalError_LoadModelFile_S, str);
}
@ -662,7 +669,27 @@ void ProcessModelFaceMaterials2(br_model* pModel, tPMFM2CB pCallback) {
tU16 group;
br_material* old_mat;
LOG_TRACE("(%p, %d)", pModel, pCallback);
NOT_IMPLEMENTED();
if (pModel->faces) {
for (f = 0; f < pModel->nfaces; f++) {
if (pModel->faces[f].material) {
pCallback(pModel->faces[f].material);
}
}
} else {
if (pModel->prepared == NULL) {
return;
}
for (group = 0; group < V11MODEL(pModel)->ngroups; group++) {
for (f = 0; f < V11MODEL(pModel)->groups[group].nfaces; f++) {
// old_mat = V11MODEL(pModel)->groups[group].face_colours[f];
old_mat = V11MODEL(pModel)->groups[group].user;
if (old_mat) {
pCallback(old_mat);
}
}
}
}
}
// IDA: void __usercall ProcessModelFaceMaterials(br_model *pModel@<EAX>, tPMFMCB pCallback@<EDX>)
@ -711,6 +738,9 @@ int LoadNTrackModels(tBrender_storage* pStorage_space, FILE* pF, int pCount) {
if (total == 0) {
FatalError(kFatalError_LoadModelFile_S, str);
}
#ifdef DETHRACE_3DFX_PATCH
WhitenVertexRGB(temp_array, total);
#endif
for (j = 0; j < total; j++) {
if (temp_array[j]) {
switch (AddModelToStorage(pStorage_space, temp_array[j])) {
@ -1050,7 +1080,20 @@ br_uint_32 AddProximities(br_actor* pActor, br_material* pMat, tFunkotronic_spec
void Adjust2FloatsForExceptions(float* pVictim1, float* pVictim2, br_pixelmap* pCulprit) {
tException_list e;
LOG_TRACE("(%p, %p, %p)", pVictim1, pVictim2, pCulprit);
NOT_IMPLEMENTED();
if (pCulprit && pCulprit->identifier != NULL) {
e = FindExceptionInList(pCulprit->identifier, gExceptions);
if (e) {
if ((e->flags & ExceptionFlag_Double) != 0) {
*pVictim1 = *pVictim1 * 2.0f;
*pVictim2 = *pVictim2 * 2.0f;
}
if ((e->flags & ExceptionFlag_Quadruple) != 0) {
*pVictim1 = *pVictim1 * 4.0f;
*pVictim2 = *pVictim2 * 4.0f;
}
}
}
}
// IDA: void __usercall AddFunkotronics(FILE *pF@<EAX>, int pOwner@<EDX>, int pRef_offset@<EBX>)
@ -1204,6 +1247,10 @@ void AddFunkotronics(FILE* pF, int pOwner, int pRef_offset) {
the_funk->matrix_mod_data.roll_info.x_period = speed1 == 0.0f ? 0.0f : 1000.0f / speed1;
the_funk->matrix_mod_data.roll_info.y_period = speed2 == 0.0f ? 0.0f : 1000.0f / speed2;
}
#ifdef DETHRACE_3DFX_PATCH
Adjust2FloatsForExceptions(&the_funk->matrix_mod_data.roll_info.x_period, &the_funk->matrix_mod_data.roll_info.y_period, the_funk->material->colour_map);
#endif
break;
default:
break;
@ -1267,11 +1314,18 @@ void AddFunkotronics(FILE* pF, int pOwner, int pRef_offset) {
193);
}
the_pixelmap = DRPixelmapAllocate(
#ifdef DETHRACE_3DFX_PATCH
BR_PMT_INDEX_8,
#else
gScreen->type,
#endif
the_funk->texture_animation_data.flic_info.flic_descriptor.width,
the_funk->texture_animation_data.flic_info.flic_descriptor.height,
the_pixels,
0);
#ifdef DETHRACE_3DFX_PATCH
the_pixelmap = PurifiedPixelmap(the_pixelmap);
#endif
AssertFlicPixelmap(&the_funk->texture_animation_data.flic_info.flic_descriptor, the_pixelmap);
the_funk->material->colour_map = the_pixelmap;
BrMaterialUpdate(the_funk->material, BR_MATU_ALL);
@ -1826,7 +1880,15 @@ void SaveAdditionalStuff(void) {
// IDA: br_uint_32 __cdecl ProcessMaterials(br_actor *pActor, tPMFM2CB pCallback)
br_uint_32 ProcessMaterials(br_actor* pActor, tPMFM2CB pCallback) {
LOG_TRACE("(%p, %d)", pActor, pCallback);
NOT_IMPLEMENTED();
if (pActor->material) {
pCallback(pActor->material);
}
if (pActor->type == BR_ACTOR_MODEL && pActor->model != NULL) {
ProcessModelFaceMaterials2(pActor->model, pCallback);
}
return BrActorEnum(pActor, (br_actor_enum_cbfn*)ProcessMaterials, pCallback);
}
// IDA: br_uint_32 __cdecl ProcessFaceMaterials2(br_actor *pActor, tPMFM2CB pCallback)
@ -2340,7 +2402,9 @@ void ParseSpecialVolume(FILE* pF, tSpecial_volume* pSpec, char* pScreen_name_str
// IDA: void __usercall AddExceptionToList(tException_list *pDst@<EAX>, tException_list pNew@<EDX>)
void AddExceptionToList(tException_list* pDst, tException_list pNew) {
LOG_TRACE("(%p, %d)", pDst, pNew);
NOT_IMPLEMENTED();
pNew->next = *pDst;
*pDst = pNew;
}
// IDA: void __usercall LoadExceptionsFile(char *pName@<EAX>)
@ -2352,14 +2416,66 @@ void LoadExceptionsFile(char* pName) {
tException_list e;
char delimiters[4];
LOG_TRACE("(\"%s\")", pName);
NOT_IMPLEMENTED();
strcpy(delimiters, "\t ,");
f = DRfopen(pName, "rt");
if (f) {
GetALineAndDontArgue(f, line);
tok = strtok(line, delimiters);
if (DRStricmp(tok, "VERSION")) {
FatalError(120, pName, "VERSION");
}
tok = strtok(NULL, delimiters);
if (sscanf(tok, "%d", &file_version) == 0 || file_version != 1) {
FatalError(121, tok, pName);
}
while (1) {
GetALineAndDontArgue(f, line);
tok = strtok(line, delimiters);
if (DRStricmp(tok, "end") == 0) {
break;
}
e = BrMemAllocate(sizeof(tException_list), kMem_misc);
e->name = BrMemAllocate(strlen(tok) + 1, kMem_misc_string);
strcpy(e->name, tok);
e->flags = 0;
while (1) {
tok = strtok(NULL, delimiters);
if (tok == NULL /*|| (IsTable[(unsigned __int8)(*v11 + 1)] & 0xE0) == 0*/) {
break;
}
if (DRStricmp(tok, "mipmap") == 0) {
e->flags |= ExceptionFlag_Mipmap;
} else if (DRStricmp(tok, "nobilinear") == 0) {
e->flags |= ExceptionFlag_NoBilinear;
} else if (DRStricmp(tok, "double") == 0) {
e->flags |= ExceptionFlag_Double;
} else if (DRStricmp(tok, "quadruple") == 0) {
e->flags |= ExceptionFlag_Quadruple;
} else {
FatalError(123, tok, pName);
}
}
AddExceptionToList(&gExceptions, e);
}
fclose(f);
}
}
// IDA: void __usercall LoadExceptionsFileForTrack(char *pTrack_file_name@<EAX>)
void LoadExceptionsFileForTrack(char* pTrack_file_name) {
tPath_name exceptions_file_name;
LOG_TRACE("(\"%s\")", pTrack_file_name);
NOT_IMPLEMENTED();
sprintf(
exceptions_file_name,
"%s%s%s%s",
pTrack_file_name,
gDir_separator,
gExceptions_general_file,
gExceptions_file_suffix);
LoadExceptionsFile(exceptions_file_name);
}
// IDA: void __cdecl FreeExceptions()
@ -2367,7 +2483,17 @@ void FreeExceptions(void) {
tException_list list;
tException_list next;
LOG_TRACE("()");
NOT_IMPLEMENTED();
list = gExceptions;
if (list) {
do {
next = list->next;
BrMemFree(list->name);
BrMemFree(list);
list = next;
} while (next);
}
gExceptions = NULL;
}
// IDA: void __usercall LoadTrack(char *pFile_name@<EAX>, tTrack_spec *pTrack_spec@<EDX>, tRace_info *pRace_info@<EBX>)
@ -2418,6 +2544,9 @@ void LoadTrack(char* pFile_name, tTrack_spec* pTrack_spec, tRace_info* pRace_inf
killed_sky = 0;
PathCat(the_path, gApplication_path, "RACES");
PathCat(the_path, the_path, pFile_name);
#ifdef DETHRACE_3DFX_PATCH
LoadExceptionsFileForTrack(the_path);
#endif
f = DRfopen(the_path, "rt");
if (f == NULL) {
FatalError(kFatalError_OpenRacesFile);
@ -2524,15 +2653,20 @@ void LoadTrack(char* pFile_name, tTrack_spec* pTrack_spec, tRace_info* pRace_inf
LoadSomeMaterials(&gTrack_storage_space, f);
SkipNLines(f);
}
for (i = 0; gTrack_storage_space.materials_count > i; ++i) {
PossibleService();
if (gTrack_storage_space.materials[i]->flags & (BR_MATF_LIGHT | BR_MATF_PRELIT | BR_MATF_SMOOTH)) {
gTrack_storage_space.materials[i]->flags &= ~(BR_MATF_LIGHT | BR_MATF_PRELIT | BR_MATF_SMOOTH);
if (gTrack_storage_space.materials[i]->flags & BR_MATF_TWO_SIDED) {
gTrack_storage_space.materials[i]->user = DOUBLESIDED_USER_FLAG;
gTrack_storage_space.materials[i]->flags &= ~BR_MATF_TWO_SIDED;
#ifdef DETHRACE_3DFX_PATCH
if (!gShade_tables_do_not_work)
#endif
{
for (i = 0; i < gTrack_storage_space.materials_count; i++) {
PossibleService();
if (gTrack_storage_space.materials[i]->flags & (BR_MATF_LIGHT | BR_MATF_PRELIT | BR_MATF_SMOOTH)) {
gTrack_storage_space.materials[i]->flags &= ~(BR_MATF_LIGHT | BR_MATF_PRELIT | BR_MATF_SMOOTH);
if (gTrack_storage_space.materials[i]->flags & BR_MATF_TWO_SIDED) {
gTrack_storage_space.materials[i]->user = DOUBLESIDED_USER_FLAG;
gTrack_storage_space.materials[i]->flags &= ~BR_MATF_TWO_SIDED;
}
BrMaterialUpdate(gTrack_storage_space.materials[i], BR_MATU_RENDERING);
}
BrMaterialUpdate(gTrack_storage_space.materials[i], BR_MATU_RENDERING);
}
}
if (gRace_file_version <= 5) {
@ -2653,6 +2787,8 @@ void LoadTrack(char* pFile_name, tTrack_spec* pTrack_spec, tRace_info* pRace_inf
gProgram_state.default_depth_effect.start = 7;
gProgram_state.default_depth_effect.end = 0;
}
#ifndef DETHRACE_3DFX_PATCH
// In the 3dfx patch this code was moved into `InitRace`
InstantDepthChange(
gProgram_state.default_depth_effect.type,
gProgram_state.default_depth_effect.sky_texture,
@ -2666,6 +2802,7 @@ void LoadTrack(char* pFile_name, tTrack_spec* pTrack_spec, tRace_info* pRace_inf
if (!GetDepthCueingOn()) {
ToggleDepthCueingQuietly();
}
#endif
PossibleService();
gDefault_engine_noise_index = GetAnInt(f);
gDefault_water_spec_vol = &gDefault_default_water_spec_vol;
@ -2810,6 +2947,13 @@ void LoadTrack(char* pFile_name, tTrack_spec* pTrack_spec, tRace_info* pRace_inf
strcat(str, ".MAT");
material = LoadSingleMaterial(&gTrack_storage_space, str);
pRace_info->material_modifiers[i].skid_mark_material = material;
#ifdef DETHRACE_3DFX_PATCH
if (material != NULL) {
GlorifyMaterial(&material, 1);
BrMaterialUpdate(material, BR_MATU_ALL);
}
#endif
#if defined(DETHRACE_FIX_BUGS)
skid_mark_cnt++;
#endif
@ -2905,6 +3049,9 @@ void LoadTrack(char* pFile_name, tTrack_spec* pTrack_spec, tRace_info* pRace_inf
FatalError(kFatalError_FileCorrupt_S, pFile_name);
}
fclose(f);
#ifdef DETHRACE_3DFX_PATCH
FreeExceptions();
#endif
}
// IDA: br_uint_32 __cdecl RemoveBounds(br_actor *pActor, void *pArg)
@ -3000,46 +3147,46 @@ br_scalar NormaliseDegreeAngle(br_scalar pAngle) {
#define SAW(T, PERIOD) (fmodf((T), (PERIOD)) / (PERIOD))
#define MOVE_FUNK_PARAMETER(DEST, MODE, PERIOD, AMPLITUDE, FLASH_VALUE) \
do { \
switch (MODE) { \
case eMove_continuous: \
if ((PERIOD) == 0.f) { \
DEST = 0.f; \
} else { \
DEST = (AMPLITUDE)*SAW(f_the_time, (PERIOD)); \
} \
break; \
case eMove_controlled: \
DEST = (PERIOD) * (AMPLITUDE); \
break; \
case eMove_absolute: \
DEST = (PERIOD); \
break; \
case eMove_linear: \
if ((PERIOD) == 0.f) { \
DEST = 0.f; \
} else { \
DEST = (AMPLITUDE)*MapSawToTriangle(SAW(f_the_time, (PERIOD))); \
} \
break; \
case eMove_flash: \
if (2 * fmodf(f_the_time, (PERIOD)) > (PERIOD)) { \
DEST = (FLASH_VALUE); \
} else { \
DEST = -(FLASH_VALUE); \
} \
break; \
case eMove_harmonic: \
if ((PERIOD) == 0.f) { \
DEST = 0.f; \
} else { \
DEST = (AMPLITUDE)*BR_SIN(BR_ANGLE_DEG(SAW(f_the_time, (PERIOD)) * 360)); \
} \
break; \
default: \
TELL_ME_IF_WE_PASS_THIS_WAY(); \
} \
#define MOVE_FUNK_PARAMETER(DEST, MODE, PERIOD, AMPLITUDE, FLASH_VALUE) \
do { \
switch (MODE) { \
case eMove_continuous: \
if ((PERIOD) == 0.f) { \
DEST = 0.f; \
} else { \
DEST = (AMPLITUDE) * SAW(f_the_time, (PERIOD)); \
} \
break; \
case eMove_controlled: \
DEST = (PERIOD) * (AMPLITUDE); \
break; \
case eMove_absolute: \
DEST = (PERIOD); \
break; \
case eMove_linear: \
if ((PERIOD) == 0.f) { \
DEST = 0.f; \
} else { \
DEST = (AMPLITUDE) * MapSawToTriangle(SAW(f_the_time, (PERIOD))); \
} \
break; \
case eMove_flash: \
if (2 * fmodf(f_the_time, (PERIOD)) > (PERIOD)) { \
DEST = (FLASH_VALUE); \
} else { \
DEST = -(FLASH_VALUE); \
} \
break; \
case eMove_harmonic: \
if ((PERIOD) == 0.f) { \
DEST = 0.f; \
} else { \
DEST = (AMPLITUDE) * BR_SIN(BR_ANGLE_DEG(SAW(f_the_time, (PERIOD)) * 360)); \
} \
break; \
default: \
TELL_ME_IF_WE_PASS_THIS_WAY(); \
} \
} while (0)
// IDA: void __cdecl FunkThoseTronics()

View File

@ -789,6 +789,14 @@ enum {
NETMSGID_NONE = 0x20,
};
// Introduced with 3DFX patch
enum ExceptionFlags {
ExceptionFlag_NoBilinear = 1,
ExceptionFlag_Double = 2,
ExceptionFlag_Quadruple = 4,
ExceptionFlag_Mipmap = 4,
};
#define FONT_TYPEABLE 0
#define FONT_ORANGHED 1
#define FONT_BLUEHEAD 2
@ -838,4 +846,6 @@ enum {
// average frame time in carm95
#define MUNGE_ENGINE_INTERVAL 50
#define HIRES_Y_OFFSET 40
#endif

View File

@ -15,10 +15,13 @@ extern int original_main(int pArgc, char* pArgv[]);
void BR_CALLBACK _BrBeginHook(void) {
struct br_device* BR_EXPORT BrDrv1SoftPrimBegin(char* arguments);
struct br_device* BR_EXPORT BrDrv1SoftRendBegin(char* arguments);
struct br_device* BR_EXPORT BrDrv1VirtualFramebufferBegin(char* arguments);
struct br_device* BR_EXPORT BrDrv1GLBegin(char* arguments);
BrDevAddStatic(NULL, BrDrv1SoftPrimBegin, NULL);
BrDevAddStatic(NULL, BrDrv1SoftRendBegin, NULL);
// BrDevAddStatic(NULL, BrDrv1SDL2Begin, NULL);
BrDevAddStatic(NULL, BrDrv1VirtualFramebufferBegin, NULL);
BrDevAddStatic(NULL, BrDrv1GLBegin, NULL);
}
void BR_CALLBACK _BrEndHook(void) {

View File

@ -2,15 +2,18 @@
#include "car.h"
#include "errors.h"
#include "globvars.h"
#include "globvrbm.h"
#include "grafdata.h"
#include "graphics.h"
#include "harness/config.h"
#include "harness/hooks.h"
#include "harness/os.h"
#include "harness/trace.h"
#include "init.h"
#include "input.h"
#include "loadsave.h"
#include "main.h"
#include "pc-dos/scancodes.h"
#include "pd/sys.h"
#include "sound.h"
#include "utility.h"
@ -21,17 +24,58 @@
#include <sys/stat.h>
#include <time.h>
#ifdef __DOS__
#define GFX_INIT_STRING_32X20X8 "MCGA,W:320,H:200,B:8"
#define GFX_INIT_STRING_64X48X8 "VESA,W:640,H:480,B:8"
// This code comes from DOS, so small changes need to be made to run correctly on windowed systems.
// Generally the pc-win95 does the same thing
#define PLAY_NICE_WITH_GUI 1
#ifdef PLAY_NICE_WITH_GUI
#define MOUSE_SPEED_MULTIPLIER 1
#else
#define MOUSE_SPEED_MULTIPLIER 0.25f
#endif
int gDOSGfx_initialized;
int gExtra_mem;
int gReplay_override;
tGraf_spec gGraf_specs[2] = {
{ 8, 1, 0, 320, 200, 0, 0, "32X20X8", "MCGA,W:320,H:200,B:8", 320, 320, 200, NULL },
{ 8, 1, 0, 640, 480, 0, 0, "64X48X8", "VESA,W:640,H:480,B:8", 640, 640, 480, NULL }
// { 8, 1, 0, 1920, 1080, 0, 0, "64X48X8", "VESA,W:640,H:480,B:8", 640, 1920, 1080, NULL }
};
int gASCII_table[128];
tU32 gKeyboard_bits[8];
int gASCII_shift_table[128];
char gNetwork_profile_fname[256];
tS32 gJoystick_min1y;
tS32 gJoystick_min2y;
tS32 gJoystick_min2x;
tS32 gRaw_joystick2y;
tS32 gRaw_joystick2x;
tS32 gRaw_joystick1y;
tS32 gRaw_joystick1x;
tS32 gJoystick_range2y;
tS32 gJoystick_range2x;
tS32 gJoystick_range1y;
tS32 gJoystick_range1x;
int gNo_voodoo;
int gSwitched_resolution;
br_pixelmap* gReal_back_screen;
tS32 gJoystick_min1x;
br_pixelmap* gTemp_screen;
tU32 gUpper_loop_limit;
int gReal_back_screen_locked;
void (*gPrev_keyboard_handler)();
void (*gPrev_keyboard_handler)(void);
tU8 gScan_code[123][2];
char* _unittest_last_fatal_error;
// Added from retail executable
int gForce_voodoo_rush_mode;
int gForce_voodoo_mode;
br_device_gl_callback_procs gl_callbacks;
br_device_virtualfb_callback_procs virtualfb_callbacks;
// forward declare for `PDInitialiseSystem`
int InitJoysticks(void);
// IDA: void __cdecl KeyboardHandler()
void KeyboardHandler(void) {
@ -44,7 +88,7 @@ void KeyboardHandler(void) {
// IDA: int __usercall KeyDown@<EAX>(tU8 pScan_code@<EAX>)
int KeyDown(tU8 pScan_code) {
NOT_IMPLEMENTED();
return (gKeyboard_bits[pScan_code >> 5] >> (pScan_code & 0x1F)) & 1;
}
// IDA: void __usercall KeyTranslation(tU8 pKey_index@<EAX>, tU8 pScan_code_1@<EDX>, tU8 pScan_code_2@<EBX>)
@ -55,19 +99,138 @@ void KeyTranslation(tU8 pKey_index, tU8 pScan_code_1, tU8 pScan_code_2) {
// IDA: void __cdecl KeyBegin()
void KeyBegin(void) {
NOT_IMPLEMENTED();
gScan_code[KEY_0][0] = SCANCODE_0;
gScan_code[KEY_2][0] = SCANCODE_2;
gScan_code[KEY_3][0] = SCANCODE_3;
gScan_code[KEY_1][0] = SCANCODE_1;
gScan_code[KEY_4][0] = SCANCODE_4;
gScan_code[KEY_6][0] = SCANCODE_6;
gScan_code[KEY_7][0] = SCANCODE_7;
gScan_code[KEY_5][0] = SCANCODE_5;
gScan_code[KEY_8][0] = SCANCODE_8;
gScan_code[KEY_A][0] = SCANCODE_A;
gScan_code[KEY_B][0] = SCANCODE_B;
gScan_code[KEY_9][0] = SCANCODE_9;
gScan_code[KEY_C][0] = SCANCODE_C;
gScan_code[KEY_E][0] = SCANCODE_E;
gScan_code[KEY_F][0] = SCANCODE_F;
gScan_code[KEY_D][0] = SCANCODE_D;
gScan_code[KEY_G][0] = SCANCODE_G;
gScan_code[KEY_I][0] = SCANCODE_I;
gScan_code[KEY_J][0] = SCANCODE_J;
gScan_code[KEY_H][0] = SCANCODE_H;
gScan_code[KEY_K][0] = SCANCODE_K;
gScan_code[KEY_M][0] = SCANCODE_M;
gScan_code[KEY_N][0] = SCANCODE_N;
gScan_code[KEY_L][0] = SCANCODE_L;
gScan_code[KEY_O][0] = SCANCODE_O;
gScan_code[KEY_Q][0] = SCANCODE_Q;
gScan_code[KEY_R][0] = SCANCODE_R;
gScan_code[KEY_P][0] = SCANCODE_P;
gScan_code[KEY_S][0] = SCANCODE_S;
gScan_code[KEY_U][0] = SCANCODE_U;
gScan_code[KEY_V][0] = SCANCODE_V;
gScan_code[KEY_T][0] = SCANCODE_T;
gScan_code[KEY_W][0] = SCANCODE_W;
gScan_code[KEY_X][0] = SCANCODE_X;
gScan_code[KEY_Y][0] = SCANCODE_Y;
gScan_code[KEY_Z][0] = SCANCODE_Z;
gScan_code[KEY_GRAVE][0] = SCANCODE_GRAVE;
gScan_code[KEY_MINUS][0] = SCANCODE_MINUS;
gScan_code[KEY_EQUALS][0] = SCANCODE_EQUALS;
gScan_code[KEY_BACKSPACE][0] = SCANCODE_BACK;
gScan_code[KEY_RETURN][0] = SCANCODE_RETURN;
gScan_code[KEY_KP_ENTER][0] = SCANCODE_NUMPADENTER;
gScan_code[KEY_SHIFT_ANY][0] = SCANCODE_LSHIFT;
gScan_code[KEY_SHIFT_ANY][1] = SCANCODE_RSHIFT;
gScan_code[KEY_ALT_ANY][0] = SCANCODE_LALT;
gScan_code[KEY_ALT_ANY][1] = SCANCODE_RALT;
gScan_code[KEY_CTRL_ANY][0] = SCANCODE_LCONTROL;
gScan_code[KEY_CTRL_ANY][1] = SCANCODE_RCONTROL;
gScan_code[KEY_CTRL_ANY_2][0] = SCANCODE_LCONTROL;
gScan_code[KEY_CTRL_ANY_2][1] = SCANCODE_RCONTROL;
gScan_code[KEY_CAPSLOCK][0] = SCANCODE_CAPITAL;
gScan_code[KEY_UNKNOWN_55][0] = SCANCODE_OEM_102;
gScan_code[KEY_SLASH][0] = SCANCODE_SLASH;
gScan_code[KEY_SEMICOLON][0] = SCANCODE_SEMICOLON;
gScan_code[KEY_COMMA][0] = SCANCODE_COMMA;
gScan_code[KEY_TAB][0] = SCANCODE_TAB;
gScan_code[KEY_PERIOD][0] = SCANCODE_PERIOD;
gScan_code[KEY_LBRACKET][0] = SCANCODE_LBRACKET;
gScan_code[KEY_ESCAPE][0] = SCANCODE_ESCAPE;
gScan_code[KEY_APOSTROPHE][0] = SCANCODE_APOSTROPHE;
gScan_code[KEY_BACKSLASH][0] = SCANCODE_BACKSLASH;
gScan_code[KEY_INSERT][0] = SCANCODE_INSERT;
gScan_code[KEY_END][0] = SCANCODE_END;
gScan_code[KEY_RBRACKET][0] = SCANCODE_RBRACKET;
gScan_code[KEY_HOME][0] = SCANCODE_HOME;
gScan_code[KEY_PAGEUP][0] = SCANCODE_PGUP;
gScan_code[KEY_RIGHT][0] = SCANCODE_RIGHT;
gScan_code[KEY_DELETE][0] = SCANCODE_DELETE;
gScan_code[KEY_LEFT][0] = SCANCODE_LEFT;
gScan_code[KEY_UP][0] = SCANCODE_UP;
gScan_code[KEY_PAGEDOWN][0] = SCANCODE_PGDN;
gScan_code[KEY_KP_NUMLOCK][0] = SCANCODE_NUMLOCK;
gScan_code[KEY_DOWN][0] = SCANCODE_DOWN;
gScan_code[KEY_KP_DIVIDE][0] = SCANCODE_DIVIDE;
gScan_code[KEY_KP_MULTIPLY][0] = SCANCODE_MULTIPLY;
gScan_code[KEY_KP_PLUS][0] = SCANCODE_ADD;
gScan_code[KEY_KP_MINUS][0] = SCANCODE_SUBTRACT;
gScan_code[KEY_KP_EQUALS][0] = 0;
gScan_code[KEY_KP_PERIOD][0] = SCANCODE_DECIMAL;
gScan_code[KEY_KP_1][0] = SCANCODE_NUMPAD1;
gScan_code[KEY_KP_3][0] = SCANCODE_NUMPAD3;
gScan_code[KEY_KP_0][0] = SCANCODE_NUMPAD0;
gScan_code[KEY_KP_2][0] = SCANCODE_NUMPAD2;
gScan_code[KEY_KP_5][0] = SCANCODE_NUMPAD5;
gScan_code[KEY_KP_7][0] = SCANCODE_NUMPAD7;
gScan_code[KEY_KP_4][0] = SCANCODE_NUMPAD4;
gScan_code[KEY_KP_6][0] = SCANCODE_NUMPAD6;
gScan_code[KEY_KP_9][0] = SCANCODE_NUMPAD9;
gScan_code[KEY_F2][0] = SCANCODE_F2;
gScan_code[KEY_KP_8][0] = SCANCODE_NUMPAD8;
gScan_code[KEY_F1][0] = SCANCODE_F1;
gScan_code[KEY_F4][0] = SCANCODE_F4;
gScan_code[KEY_F6][0] = SCANCODE_F6;
gScan_code[KEY_F3][0] = SCANCODE_F3;
gScan_code[KEY_F5][0] = SCANCODE_F5;
gScan_code[KEY_F8][0] = SCANCODE_F8;
gScan_code[KEY_F10][0] = SCANCODE_F10;
gScan_code[KEY_F7][0] = SCANCODE_F7;
gScan_code[KEY_F9][0] = SCANCODE_F9;
gScan_code[KEY_F12][0] = SCANCODE_F12;
gScan_code[KEY_SCRLK][0] = SCANCODE_SCROLL;
gScan_code[KEY_F11][0] = SCANCODE_F11;
gScan_code[KEY_PRTSCN][0] = 0;
gScan_code[KEY_PAUSE][0] = 0;
gScan_code[KEY_SPACE][0] = SCANCODE_SPACE;
gScan_code[KEY_RSHIFT][0] = SCANCODE_RSHIFT;
gScan_code[KEY_RALT][0] = SCANCODE_RALT;
gScan_code[KEY_RCTRL][0] = SCANCODE_RCONTROL;
gScan_code[KEY_LSHIFT][0] = SCANCODE_LSHIFT;
gScan_code[KEY_LALT][0] = SCANCODE_LALT;
gScan_code[KEY_LCTRL][0] = SCANCODE_LCONTROL;
// gPrev_keyboard_handler = dos_getvect(9);
// dos_setvect(9, KeyboardHandler);
}
// IDA: void __cdecl KeyEnd()
void KeyEnd(void) {
LOG_TRACE("()");
NOT_IMPLEMENTED();
// dos_setvect(9, gPrev_keyboard_handler);
}
// IDA: int __usercall KeyDown22@<EAX>(int pKey_index@<EAX>)
int KeyDown22(int pKey_index) {
LOG_TRACE("(%d)", pKey_index);
NOT_IMPLEMENTED();
return KeyDown(gScan_code[pKey_index][0]) || KeyDown(gScan_code[pKey_index][1]);
}
// IDA: void __usercall PDSetKeyArray(int *pKeys@<EAX>, int pMark@<EDX>)
@ -76,6 +239,28 @@ void PDSetKeyArray(int* pKeys, int pMark) {
tS32 joyX;
tS32 joyY;
LOG_TRACE10("(%p, %d)", pKeys, pMark);
#ifdef PLAY_NICE_WITH_GUI
// Required in some cases like a tight loop waiting for a keypress
gHarness_platform.ProcessWindowMessages(NULL);
#endif
gKeys_pressed = 0;
for (i = 0; i < COUNT_OF(gScan_code); i++) {
if (KeyDown(gScan_code[i][0]) || KeyDown(gScan_code[i][1])) {
gKeys_pressed = i + (gKeys_pressed << 8) + 1;
pKeys[i] = pMark;
} else if (pMark == pKeys[i]) {
pKeys[i] = 0;
}
}
}
int PDGetASCIIFromKey(int pKey) {
if (PDKeyDown3(KEY_SHIFT_ANY))
return gASCII_shift_table[pKey];
else
return gASCII_table[pKey];
}
// IDA: void __usercall PDFatalError(char *pThe_str@<EAX>)
@ -87,21 +272,37 @@ void PDFatalError(char* pThe_str) {
exit(1);
}
been_here = 1;
dr_dprintf("FATAL ERROR: %s", pThe_str);
_unittest_last_fatal_error = pThe_str;
fprintf(stderr, "FATAL ERROR: %s\n", pThe_str);
// wait for keypress
abort();
if (gDOSGfx_initialized) {
gDOSGfx_initialized = 0;
BrDevEndOld();
}
printf("FATAL ERROR: %s\n", pThe_str);
dr_dprintf("FATAL ERROR: %s\n", pThe_str);
#ifdef PLAY_NICE_WITH_GUI
gHarness_platform.ShowErrorMessage(NULL, "Carmageddon Fatal Error", pThe_str);
#endif
if (gBrZb_initialized) {
gBrZb_initialized = 0;
BrZbEnd();
}
if (gBr_initialized) {
gBr_initialized = 0;
}
#ifndef PLAY_NICE_WITH_GUI
// There is no window to receive keyboard events from
while (PDAnyKeyDown() == -1) {
}
#endif
QuitGame();
}
// IDA: void __usercall PDNonFatalError(char *pThe_str@<EAX>)
void PDNonFatalError(char* pThe_str) {
LOG_TRACE("(\"%s\")", pThe_str);
NOT_IMPLEMENTED();
printf("ERROR: %s", pThe_str);
while (PDAnyKeyDown() == -1) {
}
}
// IDA: void __cdecl PDInitialiseSystem()
@ -112,9 +313,8 @@ void PDInitialiseSystem(void) {
KeyBegin();
// v4 = DOSMouseBegin();
gJoystick_deadzone = 8000;
// gUpper_loop_limit = sub_A1940(v4, v5, v3, v6) / 2;
// DOSMouseBegin();
InitJoysticks();
// Demo's do not ship with KEYBOARD.COK file
if (harness_game_info.defines.ascii_table == NULL) {
@ -143,16 +343,15 @@ void PDInitialiseSystem(void) {
}
// IDA: void __cdecl PDShutdownSystem()
void PDShutdownSystem() {
static int been_here = 0; // Added by dethrace
void PDShutdownSystem(void) {
LOG_TRACE("()");
if (!been_here) {
Harness_Hook_PDShutdownSystem();
} else {
LOG_WARN("recursion detected => force exit");
exit(8);
// dos_setvect(9, gPrev_keyboard_handler);
if (gDOSGfx_initialized) {
BrDevEndOld();
}
// DOSMouseEnd();
PDRevertPalette();
}
// IDA: void __cdecl PDSaveOriginalPalette()
@ -177,51 +376,121 @@ int PDInitScreenVars(int pArgc, char** pArgv) {
// IDA: void __cdecl PDInitScreen()
void PDInitScreen(void) {
LOG_TRACE("()");
}
// IDA: void __cdecl sub_B4DB4()
void sub_B4DB4(void) {
// if (!gReal_back_screen->pixels_qualifier) {
// gReal_back_screen->pixels_qualifier = (unsigned __int16)__DS__;
// }
}
// IDA: void __cdecl PDLockRealBackScreen()
void PDLockRealBackScreen(void) {
// In all retail 3dfx executables, it is void __usercall PDLockRealBackScreen(lock@<EAX>)
void PDLockRealBackScreen(int lock) {
LOG_TRACE("()");
NOT_IMPLEMENTED();
if (!gReal_back_screen_locked && !gReal_back_screen->pixels && lock <= gVoodoo_rush_mode) {
sub_B4DB4();
BrPixelmapDirectLock(gReal_back_screen, 1);
if (!gReal_back_screen->pixels)
FatalError(117, "gReal_back_screen");
gReal_back_screen_locked = 1;
}
}
// IDA: void __cdecl PDUnlockRealBackScreen()
void PDUnlockRealBackScreen(void) {
// In all retail 3dfx executables, it is void __usercall PDUnlockRealBackScreen(lock@<EAX>)
void PDUnlockRealBackScreen(int lock) {
LOG_TRACE("()");
NOT_IMPLEMENTED();
if (gReal_back_screen_locked && gReal_back_screen->pixels && lock <= gVoodoo_rush_mode) {
BrPixelmapDirectUnlock(gReal_back_screen);
gReal_back_screen_locked = 0;
}
}
// IDA: void __cdecl PDAllocateScreenAndBack()
void PDAllocateScreenAndBack(void) {
dr_dprintf("PDAllocateScreenAndBack() - START...");
BrMaterialFindHook(PDMissingMaterial);
BrTableFindHook(PDMissingTable);
BrModelFindHook(PDMissingModel);
BrMapFindHook(PDMissingMap);
gScreen = NULL;
// This is a bit of a mix between the original DOS code and windows code...
// DOS:
// gScreen = DOSGfxBegin(gGraf_specs[gGraf_spec_index].gfx_init_string);
//
// Windows:
// SSDXInit(gGraf_specs[gGraf_spec_index].total_width, gGraf_specs[gGraf_spec_index].total_height)
// gScreen = BrPixelmapAllocate(BR_PMT_INDEX_8, ...)
//
// added by dethrace. We default to software mode unless we explicitly ask for 3dfx opengl mode
if (harness_game_config.opengl_3dfx_mode) {
Harness_Hook_GraphicsInit(gGraf_specs[gGraf_spec_index].total_width, gGraf_specs[gGraf_spec_index].total_height);
gScreen = BrPixelmapAllocate(BR_PMT_INDEX_8, gGraf_specs[gGraf_spec_index].total_width, gGraf_specs[gGraf_spec_index].total_height, NULL, BR_PMAF_NORMAL);
if (gGraf_spec_index != 0 && !gNo_voodoo) {
#ifdef PLAY_NICE_WITH_GUI
gl_callbacks.get_proc_address = gHarness_platform.GL_GetProcAddress;
gl_callbacks.swap_buffers = gHarness_platform.Swap;
gl_callbacks.get_viewport = gHarness_platform.GetViewport;
gHarness_platform.CreateWindow_("Carmageddon", gGraf_specs[gGraf_spec_index].phys_width, gGraf_specs[gGraf_spec_index].phys_height, eWindow_type_opengl);
BrDevBeginVar(&gScreen, "glrend",
BRT_WIDTH_I32, gGraf_specs[gGraf_spec_index].phys_width,
BRT_HEIGHT_I32, gGraf_specs[gGraf_spec_index].phys_height,
BRT_OPENGL_CALLBACKS_P, &gl_callbacks,
BRT_PIXEL_TYPE_U8, BR_PMT_RGB_565,
BR_NULL_TOKEN);
#else
BrDevBegin(&gScreen, "3dfx_dos,w:640,h:480,b:16");
#endif
}
}
if (gScreen != NULL) {
if ((strcmp(gScreen->identifier, "Voodoo Graphics") == 0 && !gForce_voodoo_rush_mode) || gForce_voodoo_mode) {
dr_dprintf("Voodoo Graphics mode");
} else {
dr_dprintf("Voodoo Rush mode");
gVoodoo_rush_mode = 1;
}
gInterpolate_textures = 1;
gUse_mip_maps = 1;
gTextures_need_powers_of_2 = 1;
gMax_texture_aspect_ratio = 8;
gMax_texture_side = 256;
gBlitting_is_slow = 1;
gMaterial_fogging = 1;
gExceptions_general_file = "VOODOO";
gExceptions_file_suffix = ".TXT";
gSmall_frames_are_slow = 1;
gNo_2d_effects = 1;
gPerspective_is_fast = 1;
gNo_transients = 1;
gDevious_2d = 1;
gShade_tables_do_not_work = 1;
} else {
gExceptions_file_suffix = ".TXT";
gInterpolate_textures = 1;
gExceptions_general_file = "SOFTWARE";
#ifdef PLAY_NICE_WITH_GUI
// Render framebuffer to memory and call hooks when swapping or palette changing
virtualfb_callbacks.palette_changed = gHarness_platform.PaletteChanged;
virtualfb_callbacks.swap_buffers = gHarness_platform.Swap;
gHarness_platform.CreateWindow_("Carmageddon", gGraf_specs[gGraf_spec_index].phys_width, gGraf_specs[gGraf_spec_index].phys_height, eWindow_type_software);
BrDevBeginVar(&gScreen, "virtualframebuffer",
BRT_WIDTH_I32, gGraf_specs[gGraf_spec_index].phys_width,
BRT_HEIGHT_I32, gGraf_specs[gGraf_spec_index].phys_height,
BRT_VIRTUALFB_CALLBACKS_P, &virtualfb_callbacks,
BR_NULL_TOKEN);
#else
gScreen = BrDevBeginOld(gGraf_specs[gGraf_spec_index].gfx_init_string);
#endif
gDOSGfx_initialized = 1;
}
gScreen->origin_x = 0;
gDOSGfx_initialized = 1;
gScreen->origin_y = 0;
gBack_screen = BrPixelmapMatch(gScreen, BR_PMMATCH_OFFSCREEN);
gReal_back_screen = gBack_screen;
PDLockRealBackScreen(0);
gBack_screen->origin_x = 0;
gBack_screen->origin_y = 0;
gTemp_screen = BrPixelmapMatch(gScreen, BR_PMMATCH_OFFSCREEN);
gTemp_screen = BrPixelmapAllocate(BR_PMT_INDEX_8, gScreen->width, gScreen->height, 0, 0);
gTemp_screen->origin_x = 0;
gTemp_screen->origin_y = 0;
dr_dprintf("PDAllocateScreenAndBack() - END.");
}
// IDA: void __usercall Copy8BitTo16BitPixelmap(br_pixelmap *pDst@<EAX>, br_pixelmap *pSrc@<EDX>, br_pixelmap *pPalette@<EBX>)
@ -236,7 +505,18 @@ void Copy8BitTo16BitPixelmap(br_pixelmap* pDst, br_pixelmap* pSrc, br_pixelmap*
tU16* dst;
tU16* palette_entry;
LOG_TRACE("(%p, %p, %p)", pDst, pSrc, pPalette);
NOT_IMPLEMENTED();
palette_entry = PaletteOf16Bits(pPalette)->pixels;
for (y = 0; pSrc->height > y; y++) {
src = (tU8*)pSrc->pixels + pSrc->row_bytes * y;
dst = (tU16*)((tU8*)pDst->pixels + pDst->row_bytes * y);
for (x = 0; x < pSrc->width; x++) {
value = *src;
*dst = palette_entry[value];
src++;
dst++;
}
}
}
// IDA: void __usercall Double8BitTo16BitPixelmap(br_pixelmap *pDst@<EAX>, br_pixelmap *pSrc@<EDX>, br_pixelmap *pPalette@<EBX>, tU16 pOff@<ECX>, tU16 pSrc_width, tU16 pSrc_height)
@ -253,7 +533,36 @@ void Double8BitTo16BitPixelmap(br_pixelmap* pDst, br_pixelmap* pSrc, br_pixelmap
tU16 sixteen;
tU16* palette_entry;
LOG_TRACE("(%p, %p, %p, %d, %d, %d)", pDst, pSrc, pPalette, pOff, pSrc_width, pSrc_height);
NOT_IMPLEMENTED();
// added by dethrace. Some local symbols seem to be missing
int dst_y = 0;
int line_buff_x = 0;
static tU16 line_buff[640];
palette_entry = PaletteOf16Bits(pPalette)->pixels;
if (pSrc_width > 640) {
FatalError(94, "Double8BitTo16BitPixelmap");
}
dst_y = 0;
for (y = 0; y < pSrc_height; y++) {
src = (tU8*)pSrc->pixels + pSrc->row_bytes * y;
dst0 = (tU16*)((tU8*)pDst->pixels + pDst->row_bytes * (dst_y + pOff));
dst1 = (tU16*)((tU8*)pDst->pixels + pDst->row_bytes * (dst_y + pOff + 1));
line_buff_x = 0;
for (x = 0; x < pSrc_width; x++) {
sixteen = palette_entry[*src];
line_buff[line_buff_x] = sixteen;
line_buff[line_buff_x + 1] = sixteen;
src++;
line_buff_x += 2;
}
// copy 2 full lines into destination
memcpy(dst0, line_buff, pSrc_width * 2 * sizeof(tU16));
memcpy(dst1, line_buff, pSrc_width * 2 * sizeof(tU16));
dst_y += 2;
}
}
// IDA: br_pixelmap* __cdecl PDInterfacePixelmap()
@ -265,33 +574,50 @@ br_pixelmap* PDInterfacePixelmap(void) {
// IDA: void __cdecl SwapBackScreen()
void SwapBackScreen(void) {
LOG_TRACE("()");
NOT_IMPLEMENTED();
PDUnlockRealBackScreen(1);
BrPixelmapDoubleBuffer(gScreen, gReal_back_screen);
PDLockRealBackScreen(1);
}
// IDA: void __usercall ReallyCopyBackScreen(int pRendering_area_only@<EAX>, int pClear_top_and_bottom@<EDX>)
void ReallyCopyBackScreen(int pRendering_area_only, int pClear_top_and_bottom) {
LOG_TRACE("(%d, %d)", pRendering_area_only, pClear_top_and_bottom);
NOT_IMPLEMENTED();
gAlready_copied = 1;
if (pRendering_area_only) {
BrPixelmapRectangleCopy(gScreen, gX_offset, gY_offset, gRender_screen, 0, 0, gWidth, gHeight);
} else if (gReal_graf_data_index != gGraf_data_index) {
BrPixelmapRectangleFill(gReal_back_screen, 0, 0, 640, 40, 0);
BrPixelmapRectangleFill(gReal_back_screen, 0, 440, 640, 40, 0);
if (gReal_back_screen->type == BR_PMT_RGB_565) {
Double8BitTo16BitPixelmap(gReal_back_screen, gBack_screen, gCurrent_palette, 40, 320, 200);
} else {
DRPixelmapDoubledCopy(gReal_back_screen, gBack_screen, 320, 200, 0, 40);
}
}
}
// IDA: void __usercall CopyBackScreen(int pRendering_area_only@<EAX>)
void CopyBackScreen(int pRendering_area_only) {
LOG_TRACE("(%d)", pRendering_area_only);
NOT_IMPLEMENTED();
ReallyCopyBackScreen(pRendering_area_only, 1);
}
// IDA: void __usercall PDScreenBufferSwap(int pRendering_area_only@<EAX>)
void PDScreenBufferSwap(int pRendering_area_only) {
LOG_TRACE10("(%d)", pRendering_area_only);
if (pRendering_area_only) {
BrPixelmapRectangleCopy(gScreen, gY_offset, gX_offset, gRender_screen, 0, 0, gWidth, gHeight);
} else {
if (gReal_graf_data_index == gGraf_data_index) {
BrPixelmapDoubleBuffer(gScreen, gBack_screen);
} else {
DRPixelmapDoubledCopy(gTemp_screen, gBack_screen, 320, 200, 0, 40);
BrPixelmapDoubleBuffer(gScreen, gTemp_screen);
}
if (gSwitched_resolution) {
BrPixelmapFill(gTemp_screen, 0);
}
if (!gAlready_copied) {
ReallyCopyBackScreen(pRendering_area_only, 0);
}
gAlready_copied = 0;
if (!pRendering_area_only) {
SwapBackScreen();
}
}
@ -321,12 +647,9 @@ void PDInstallErrorHandlers(void) {
// IDA: void __cdecl PDSetFileVariables()
void PDSetFileVariables(void) {
gDir_separator[0] = '\\';
// Added >>
// gDir_separator[0] = '\\';
gDir_separator[0] = '/';
gDir_separator[1] = '\0';
// <<
}
// IDA: void __usercall PDBuildAppPath(char *pThe_path@<EAX>)
@ -334,7 +657,8 @@ void PDBuildAppPath(char* pThe_path) {
int pos;
getcwd(pThe_path, 256);
strcat(pThe_path, "/"); // original: pThe_path[pos] = '\\';
// strcat(pThe_path, "\\");
strcat(pThe_path, "/");
strcpy(gNetwork_profile_fname, pThe_path);
strcat(gNetwork_profile_fname, "NETWORK.INI");
}
@ -361,7 +685,9 @@ void PDSetPalette(br_pixelmap* pThe_palette) {
void PDSetPaletteEntries(br_pixelmap* pPalette, int pFirst_colour, int pCount) {
int i;
tU8* p;
p = (tU8*)pPalette->pixels + 4 * pFirst_colour;
p = pPalette->pixels;
p += pFirst_colour * 4;
for (i = pFirst_colour; i < pFirst_colour + pCount; i++) {
#if BR_ENDIAN_BIG
BrDevPaletteSetEntryOld(i, (p[1] << 16) | (p[2] << 8) | p[3]);
@ -375,11 +701,17 @@ void PDSetPaletteEntries(br_pixelmap* pPalette, int pFirst_colour, int pCount) {
// IDA: void __cdecl PDSwitchToRealResolution()
void PDSwitchToRealResolution(void) {
LOG_TRACE("()");
gBack_screen = gReal_back_screen;
gSwitched_resolution = 1;
}
// IDA: void __cdecl PDSwitchToLoresMode()
void PDSwitchToLoresMode(void) {
LOG_TRACE("()");
gBack_screen = gTemp_screen;
gSwitched_resolution = 0;
}
// IDA: void __usercall PDMouseButtons(int *pButton_1@<EAX>, int *pButton_2@<EDX>)
@ -389,7 +721,8 @@ void PDMouseButtons(int* pButton_1, int* pButton_2) {
br_int_32 mouse_y;
LOG_TRACE("(%p, %p)", pButton_1, pButton_2);
Harness_Hook_GetMouseButtons(pButton_1, pButton_2);
// DOSMouseRead(...)
gHarness_platform.GetMouseButtons(pButton_1, pButton_2);
}
// IDA: void __usercall PDGetMousePosition(int *pX_coord@<EAX>, int *pY_coord@<EDX>)
@ -403,23 +736,49 @@ void PDGetMousePosition(int* pX_coord, int* pY_coord) {
static br_int_32 mouse_y;
LOG_TRACE("(%p, %p)", pX_coord, pY_coord);
Harness_Hook_GetMousePosition(pX_coord, pY_coord);
if (gReal_graf_data_index) {
// DOSMouseRead(&mouse_x, &mouse_y, &mouse_buttons);
gHarness_platform.GetMousePosition(&mouse_x, &mouse_y);
delta_x = gGraf_data[gGraf_data_index].width * mouse_x / gGraf_data[gReal_graf_data_index].width - gMouse_last_x_coord;
delta_y = gGraf_data[gGraf_data_index].height * mouse_y / gGraf_data[gReal_graf_data_index].height - gMouse_last_y_coord;
mouse_x2 = (double)delta_x * MOUSE_SPEED_MULTIPLIER;
mouse_y2 = (double)delta_y * MOUSE_SPEED_MULTIPLIER;
*pX_coord = gMouse_last_x_coord + mouse_x2;
*pY_coord = gMouse_last_y_coord + mouse_y2;
} else {
mouse_x = gMouse_last_x_coord;
mouse_y = gMouse_last_y_coord;
// DOSMouseRead(&mouse_x, &mouse_y, &mouse_buttons);
gHarness_platform.GetMousePosition(&mouse_x, &mouse_y);
delta_x = mouse_x - gMouse_last_x_coord;
delta_y = mouse_y - gMouse_last_y_coord;
*pX_coord = gMouse_last_x_coord + (MOUSE_SPEED_MULTIPLIER * delta_x);
*pY_coord = gMouse_last_y_coord + (MOUSE_SPEED_MULTIPLIER * delta_y);
}
}
// IDA: int __cdecl PDGetTotalTime()
int PDGetTotalTime(void) {
return OS_GetTime();
return gHarness_platform.GetTicks();
}
// IDA: int __usercall PDServiceSystem@<EAX>(tU32 pTime_since_last_call@<EAX>)
int PDServiceSystem(tU32 pTime_since_last_call) {
Harness_Hook_PDServiceSystem();
#ifdef PLAY_NICE_WITH_GUI
// Added by dethrace. Win95 code does the same
gHarness_platform.ProcessWindowMessages(NULL);
#endif
return 0;
}
// IDA: tU32 __cdecl LargestBlockAvail()
tU32 LargestBlockAvail(void) {
SREGS sregs;
// SREGS sregs;
tMem_info mem_info;
size_t memmax;
@ -441,19 +800,60 @@ void PDAllocateActionReplayBuffer(char** pBuffer, tU32* pBuffer_size) {
tU32 required;
LOG_TRACE("(%p, %p)", pBuffer, pBuffer_size);
OS_AllocateActionReplayBuffer(pBuffer, pBuffer_size);
lba = LargestBlockAvail();
if (gReplay_override) {
*pBuffer = 0;
*pBuffer_size = 0;
} else {
if (gGraf_spec_index) {
required = 600000;
} else {
required = 300000;
}
required += gExtra_mem;
if (lba >= required + 65536) {
required = lba - required;
} else {
required = 65536;
}
dr_dprintf("Allocated %u bytes to the action replay buffer for %s-res", required, gGraf_spec_index != 0 ? "high" : "low");
*pBuffer = malloc(required);
*pBuffer_size = required;
}
}
// IDA: void __usercall PDDisposeActionReplayBuffer(char *pBuffer@<EAX>)
void PDDisposeActionReplayBuffer(char* pBuffer) {
LOG_TRACE("(\"%s\")", pBuffer);
free(pBuffer);
}
// IDA: void __usercall Usage(char *pProgpath@<EAX>)
void Usage(char* pProgpath) {
// char basename[9];
char basename[256]; // fix: changed from 9 to avoid overflow on longer filenames
char* basename; // changed to support longer names
basename = OS_Basename(pProgpath);
#ifdef DETHRACE_3DFX_PATCH
fprintf(stderr, "Usage: %s options\n", basename);
fprintf(stderr, "E.G. %s %s 0.5 %s 0 %s 2 %s\n", basename, "-yon", "-simple", "-sound", "-spamfritter");
fprintf(stderr, "Valid options are:\n");
fprintf(stderr, "%s: force 640x480\n", "-hires");
fprintf(stderr, "%s number: yon factor (between 0 and 1)\n", "-yon");
fprintf(stderr, "%s number: car simplification level (integer between 0 and %d)\n", "-simple", 4);
fprintf(stderr, "%s number: sound detail level (integer between 0 and 2)\n", "-sound");
fprintf(stderr, "%s\n", "-robots");
fprintf(stderr, "%s: force low memory mode\n", "-lomem");
fprintf(stderr, "%s\n", "-nosound");
fprintf(stderr, "%s: optimal spam frittering\n", "-spamfritter");
fprintf(stderr, "%s\n", "-nocutscenes");
fprintf(stderr, "%s\n", "-noreplay");
fprintf(stderr, "%s\n", "-novoodoo");
fprintf(stderr, "%s: force Voodoo Graphics mode\n", "-vgraphics");
fprintf(stderr, "%s: force Voodoo Rush (or Voodoo 2) mode\n", "-vrush");
#else
fprintf(stderr,
"Usage: %s [%s] [%s YonFactor] [%s CarSimplificationLevel] [%s SoundDetailLevel] [%s] [%s] [%s] [%s] [%s] [%s]\nWhere YonFactor is between 0 and 1,\nCarSimplificationLevel is a whole number between 0 and %d,\nand SoundDetailLevel is a whole number.\n",
basename,
@ -468,6 +868,7 @@ void Usage(char* pProgpath) {
"-nocutscenes",
"-noreplay",
CAR_MAX_SIMPLIFICATION_LEVEL);
#endif
exit(1);
}
@ -477,6 +878,12 @@ int original_main(int pArgc, char** pArgv) {
int i;
float f;
// dethrace: added to default the software rendering mode
if (!harness_game_config.opengl_3dfx_mode) {
gNo_voodoo = 1;
}
//-
for (i = 1; i < pArgc; i++) {
if (strcasecmp(pArgv[i], "-hires") == 0) {
gGraf_spec_index = 1;
@ -509,11 +916,25 @@ int original_main(int pArgc, char** pArgv) {
gCut_scene_override = 1;
} else if (strcasecmp(pArgv[i], "-noreplay") == 0) {
gReplay_override = 1;
} else if (strcasecmp(pArgv[i], "-novoodoo") == 0) {
gNo_voodoo = 1;
} else if (strcasecmp(pArgv[i], "-vrush") == 0) {
gForce_voodoo_mode = 0;
gForce_voodoo_rush_mode = 1;
} else if (strcasecmp(pArgv[i], "-vgraphics") == 0) {
gForce_voodoo_rush_mode = 0;
gForce_voodoo_mode = 1;
} else {
Usage(pArgv[0]);
}
}
#ifdef DETHRACE_3DFX_PATCH
if (!gNo_voodoo) {
gGraf_spec_index = 1;
}
#endif
GameMain(pArgc, pArgv);
return 0;
}
@ -530,13 +951,7 @@ void PDEnterDebugger(char* pStr) {
static unsigned char* save_it;
LOG_TRACE("(\"%s\")", pStr);
// FIXME: uses __CrtDbgReport when using MSVC runtime
STUB_ONCE();
dr_dprintf("PDEnterDebugger(): %s", pStr);
// ShowCursor(1);
abort();
// ShowCursor(0);
save_it = (unsigned char*)pStr;
}
// IDA: void __cdecl PDEndItAllAndReRunTheBastard()
@ -551,20 +966,40 @@ int LoopLimitTooLow(tU32 limit) {
tU32 count;
tU32 val;
LOG_TRACE("(%d)", limit);
NOT_IMPLEMENTED();
// v2 = j___clock(limit);
// v3 = v2;
// for (count = 0; count < limit; gRaw_joystick1x += v2) {
// v5 = __inbyte(0x201u);
// v2 = v5 & 1;
// ++count;
// }
// return j___clock(v2) < (unsigned int)(v3 + 3);
return 0;
}
// IDA: tS32 __cdecl UpperLoopLimit()
tS32 UpperLoopLimit(void) {
tU32 limit;
LOG_TRACE("()");
NOT_IMPLEMENTED();
for (limit = 1024; 2 * limit && LoopLimitTooLow(limit); limit *= 2)
;
while (2 * limit && LoopLimitTooLow(limit))
limit *= 2;
while (2 * limit && LoopLimitTooLow(limit))
limit *= 2;
return limit - 1;
}
// IDA: int __cdecl InitJoysticks()
int InitJoysticks(void) {
LOG_TRACE("()");
NOT_IMPLEMENTED();
gJoystick_deadzone = 8000;
gUpper_loop_limit = UpperLoopLimit() / 2;
return 0;
}
// IDA: tU32 __usercall ReadJoystickAxis@<EAX>(int pBit@<EAX>)
@ -670,6 +1105,7 @@ int PDFileUnlock(char* pThe_path) {
}
// IDA: void __cdecl CriticalISR(INTPACK pRegs)
typedef void* INTPACK;
void CriticalISR(INTPACK pRegs) {
LOG_TRACE("(%d)", pRegs);
NOT_IMPLEMENTED();
@ -678,7 +1114,7 @@ void CriticalISR(INTPACK pRegs) {
// IDA: int __usercall PDCheckDriveExists2@<EAX>(char *pThe_path@<EAX>, char *pFile_name@<EDX>, tU32 pMin_size@<EBX>)
int PDCheckDriveExists2(char* pThe_path, char* pFile_name, tU32 pMin_size) {
struct stat buf;
void (*old_critical_isr)();
void (*old_critical_isr)(void);
int stat_failed;
char slasher[4];
char the_path[256];
@ -716,8 +1152,6 @@ int PDDoWeLeadAnAustereExistance(void) {
return block < 15000000;
}
#endif
int CheckGorePasswordFile(char* pPassword) {
tPath_name path;
FILE* f;

View File

@ -0,0 +1,106 @@
#ifndef SCANCODES_H
#define SCANCODES_H
#define SCANCODE_ESCAPE 0x01
#define SCANCODE_1 0x02
#define SCANCODE_2 0x03
#define SCANCODE_3 0x04
#define SCANCODE_4 0x05
#define SCANCODE_5 0x06
#define SCANCODE_6 0x07
#define SCANCODE_7 0x08
#define SCANCODE_8 0x09
#define SCANCODE_9 0x0A
#define SCANCODE_0 0x0B
#define SCANCODE_MINUS 0x0C /* - on main keyboard */
#define SCANCODE_EQUALS 0x0D
#define SCANCODE_BACK 0x0E /* backspace */
#define SCANCODE_TAB 0x0F
#define SCANCODE_Q 0x10
#define SCANCODE_W 0x11
#define SCANCODE_E 0x12
#define SCANCODE_R 0x13
#define SCANCODE_T 0x14
#define SCANCODE_Y 0x15
#define SCANCODE_U 0x16
#define SCANCODE_I 0x17
#define SCANCODE_O 0x18
#define SCANCODE_P 0x19
#define SCANCODE_LBRACKET 0x1A
#define SCANCODE_RBRACKET 0x1B
#define SCANCODE_RETURN 0x1C /* Enter on main keyboard */
#define SCANCODE_LCONTROL 0x1D
#define SCANCODE_A 0x1E
#define SCANCODE_S 0x1F
#define SCANCODE_D 0x20
#define SCANCODE_F 0x21
#define SCANCODE_G 0x22
#define SCANCODE_H 0x23
#define SCANCODE_J 0x24
#define SCANCODE_K 0x25
#define SCANCODE_L 0x26
#define SCANCODE_SEMICOLON 0x27
#define SCANCODE_APOSTROPHE 0x28
#define SCANCODE_GRAVE 0x29
#define SCANCODE_LSHIFT 0x2A
#define SCANCODE_BACKSLASH 0x2B
#define SCANCODE_Z 0x2C
#define SCANCODE_X 0x2D
#define SCANCODE_C 0x2E
#define SCANCODE_V 0x2F
#define SCANCODE_B 0x30
#define SCANCODE_N 0x31
#define SCANCODE_M 0x32
#define SCANCODE_COMMA 0x33
#define SCANCODE_PERIOD 0x34
#define SCANCODE_SLASH 0x35
#define SCANCODE_RSHIFT 0x36
#define SCANCODE_MULTIPLY 0x37 /* * on numeric keypad */
#define SCANCODE_LALT 0x38
#define SCANCODE_SPACE 0x39
#define SCANCODE_CAPITAL 0x3A
#define SCANCODE_F1 0x3B
#define SCANCODE_F2 0x3C
#define SCANCODE_F3 0x3D
#define SCANCODE_F4 0x3E
#define SCANCODE_F5 0x3F
#define SCANCODE_F6 0x40
#define SCANCODE_F7 0x41
#define SCANCODE_F8 0x42
#define SCANCODE_F9 0x43
#define SCANCODE_F10 0x44
#define SCANCODE_NUMLOCK 0x45
#define SCANCODE_SCROLL 0x46
#define SCANCODE_NUMPAD7 0x47
#define SCANCODE_NUMPAD8 0x48
#define SCANCODE_NUMPAD9 0x49
#define SCANCODE_SUBTRACT 0x4A /* - on numeric keypad */
#define SCANCODE_NUMPAD4 0x4B
#define SCANCODE_NUMPAD5 0x4C
#define SCANCODE_NUMPAD6 0x4D
#define SCANCODE_ADD 0x4E /* + on numeric keypad */
#define SCANCODE_NUMPAD1 0x4F
#define SCANCODE_NUMPAD2 0x50
#define SCANCODE_NUMPAD3 0x51
#define SCANCODE_NUMPAD0 0x52
#define SCANCODE_DECIMAL 0x53 /* . on numeric keypad */
#define SCANCODE_OEM_102 0x56 /* <> or \| on RT 102-key keyboard (Non-U.S.) */
#define SCANCODE_F11 0x57
#define SCANCODE_F12 0x58
#define SCANCODE_NUMPADENTER 0x9C /* Enter on numeric keypad */
#define SCANCODE_RCONTROL 0x9D
#define SCANCODE_DIVIDE 0xB5 /* / on numeric keypad */
#define SCANCODE_RALT 0xB8
#define SCANCODE_PAUSE 0xC5
#define SCANCODE_HOME 0xC7
#define SCANCODE_UP 0xC8
#define SCANCODE_PGUP 0xC9
#define SCANCODE_LEFT 0xCB
#define SCANCODE_RIGHT 0xCD
#define SCANCODE_END 0xCF
#define SCANCODE_DOWN 0xD0
#define SCANCODE_PGDN 0xD1
#define SCANCODE_INSERT 0xD2
#define SCANCODE_DELETE 0xD3
#endif

View File

@ -1,3 +1,4 @@
#if 0
#include "ssdx.h"
#include "errors.h"
#include "harness/hooks.h"
@ -62,3 +63,5 @@ void SSDXHandleError(int error) {
void SSDXSetPaleeteEntries(PALETTEENTRY_* palette, int pFirst_color, int pCount) {
DirectDrawDevice_SetPaletteEntries(palette, pFirst_color, pCount);
}
#endif

View File

@ -1,3 +1,4 @@
#if 0
#include "brender.h"
#include "car.h"
#include "dinput.h"
@ -29,6 +30,34 @@
#define KEYDOWN(var, key) (var[key] & 0x80)
// int gExtra_mem;
// int gReplay_override;
// tGraf_spec gGraf_specs[2] int gASCII_table[128];
// tU32 gKeyboard_bits[8];
// int gASCII_shift_table[128];
// char gNetwork_profile_fname[256];
// tS32 gJoystick_min1y;
// tS32 gJoystick_min2y;
// tS32 gJoystick_min2x;
// tS32 gRaw_joystick2y;
// tS32 gRaw_joystick2x;
// tS32 gRaw_joystick1y;
// tS32 gRaw_joystick1x;
// tS32 gJoystick_range2y;
// tS32 gJoystick_range2x;
// tS32 gJoystick_range1y;
// tS32 gJoystick_range1x;
// int gNo_voodoo;
// int gSwitched_resolution;
// br_pixelmap* gReal_back_screen;
// tS32 gJoystick_min1x;
// br_pixelmap* gTemp_screen;
// tU32 gUpper_loop_limit;
// int gReal_back_screen_locked;
// tU32 gScan_code[123]; // was tU8 [123][2] in symbol dump
int gDOSGfx_initialized;
int gExtra_mem;
int gReplay_override;
tGraf_spec gGraf_specs[2] = {
@ -55,9 +84,9 @@ int gSwitched_resolution;
br_pixelmap* gReal_back_screen;
tS32 gJoystick_min1x;
br_pixelmap* gTemp_screen;
int gGfx_initialized; // maybe renamed here
tU32 gUpper_loop_limit;
int gReal_back_screen_locked;
void (*gPrev_keyboard_handler)(void);
tU32 gScan_code[123]; // was tU8 [123][2] in symbol dump
// Added by dethrace. Windows-specific. Original variable names unknown.
@ -68,6 +97,7 @@ int gWin32_action_replay_buffer_allocated;
void* gWin32_action_replay_buffer;
int gWin32_action_replay_buffer_size;
void* gWin32_hwnd;
int gWin32_gfx_initialized;
int gWin32_lbutton_down;
int gWin32_rbutton_down;
PALETTEENTRY_ gWin32_palette[256];
@ -520,7 +550,7 @@ void PDAllocateScreenAndBack(void) {
gScreen = BrPixelmapAllocate(BR_PMT_INDEX_8, gGraf_specs[gGraf_spec_index].total_width, gGraf_specs[gGraf_spec_index].total_height, NULL, BR_PMAF_NORMAL);
gScreen->origin_x = 0;
gGfx_initialized = 1;
gWin32_gfx_initialized = 1;
gScreen->origin_y = 0;
gBack_screen = BrPixelmapMatch(gScreen, BR_PMMATCH_OFFSCREEN);
gBack_screen->origin_x = 0;
@ -1173,3 +1203,5 @@ void Win32BRenderFailureFunc(char* msg) {
dr_dprintf("*******************************************************************************");
Win32FatalError("BRender error detected:", msg);
}
#endif

View File

@ -16,11 +16,15 @@
#include <stdlib.h>
#include <string.h>
extern int gASCII_table[128];
extern tU32 gKeyboard_bits[8];
extern int gASCII_shift_table[128];
extern int gDOSGfx_initialized;
// extern int gExtra_mem;
// extern int gReplay_override;
extern tGraf_spec gGraf_specs[2];
extern char gNetwork_profile_fname[256];
// extern int gASCII_table[128];
// extern tU32 gKeyboard_bits[8];
// extern int gASCII_shift_table[128];
// extern char gNetwork_profile_fname[256];
extern tS32 gJoystick_min1y;
extern tS32 gJoystick_min2y;
extern tS32 gJoystick_min2x;
@ -32,22 +36,20 @@ extern tS32 gJoystick_range2y;
extern tS32 gJoystick_range2x;
extern tS32 gJoystick_range1y;
extern tS32 gJoystick_range1x;
extern int gNo_voodoo;
extern int gSwitched_resolution;
extern int gReplay_override;
// extern int gNo_voodoo;
// extern int gSwitched_resolution;
extern br_pixelmap* gReal_back_screen;
extern tS32 gJoystick_min1x;
extern br_pixelmap* gTemp_screen;
extern int gDOSGfx_initialized;
extern tU32 gUpper_loop_limit;
extern int gExtra_mem;
extern int gReal_back_screen_locked;
extern void (*gPrev_keyboard_handler)(void);
// extern br_pixelmap* gTemp_screen;
// extern tU32 gUpper_loop_limit;
// extern int gReal_back_screen_locked;
// extern void (*gPrev_keyboard_handler)(void);
// DOS
// #ifdef __DOS__
// extern tU8 gScan_code[123][2];
// Windows
extern tU32 gScan_code[123];
// #else
// extern tU32 gScan_code[123];
// #endif
extern char* _unittest_last_fatal_error;
@ -73,7 +75,7 @@ void PDNonFatalError(char* pThe_str);
void PDInitialiseSystem(void);
HARNESS_NORETURN void PDShutdownSystem(void);
void PDShutdownSystem(void);
void PDSaveOriginalPalette(void);
@ -83,9 +85,9 @@ int PDInitScreenVars(int pArgc, char** pArgv);
void PDInitScreen(void);
void PDLockRealBackScreen(void);
void PDLockRealBackScreen(int lock);
void PDUnlockRealBackScreen(void);
void PDUnlockRealBackScreen(int lock);
void PDAllocateScreenAndBack(void);
@ -99,7 +101,7 @@ br_pixelmap* PDInterfacePixelmap(void);
// void ReallyCopyBackScreen(int pRendering_area_only, int pClear_top_and_bottom);
// void CopyBackScreen(int pRendering_area_only);
void CopyBackScreen(int pRendering_area_only);
void PDScreenBufferSwap(int pRendering_area_only);

View File

@ -131,6 +131,11 @@ static void Harness_DetectGameMode(void) {
default:
break;
}
// 3dfx code paths require at least smoke.pix which is used instead of writing smoke directly to framebuffer
if (access("DATA/PIXELMAP/SMOKE.PIX", F_OK) != -1) {
harness_game_info.data_dir_has_3dfx_assets = 1;
}
}
void Harness_Init(int* argc, char* argv[]) {
@ -144,8 +149,8 @@ void Harness_Init(int* argc, char* argv[]) {
harness_game_config.enable_cd_check = 0;
// original physics time step. Lower values seem to work better at 30+ fps
harness_game_config.physics_step_time = 40;
// do not limit fps by default
harness_game_config.fps = 0;
// limit to 60 fps by default
harness_game_config.fps = 60;
// do not freeze timer
harness_game_config.freeze_timer = 0;
// default demo time out is 240s
@ -164,7 +169,6 @@ void Harness_Init(int* argc, char* argv[]) {
harness_game_config.no_bind = 0;
// Disable verbose logging
harness_game_config.verbose = 0;
// install signal handler by default
harness_game_config.install_signalhandler = 1;
@ -193,6 +197,11 @@ void Harness_Init(int* argc, char* argv[]) {
Harness_DetectGameMode();
}
if (harness_game_config.opengl_3dfx_mode && !harness_game_info.data_dir_has_3dfx_assets) {
printf("Error: data directory does not contain 3dfx assets so opengl mode cannot be used\n");
exit(1);
}
if (force_null_platform) {
Null_Platform_Init(&gHarness_platform);
} else {
@ -267,9 +276,15 @@ int Harness_ProcessCommandLine(int* argc, char* argv[]) {
} else if (strcasecmp(argv[i], "--no-bind") == 0) {
harness_game_config.no_bind = 1;
handled = 1;
} else if (strcasecmp(argv[i], "--opengl") == 0) {
harness_game_config.opengl_3dfx_mode = 1;
handled = 1;
} else if (strcasecmp(argv[i], "--no-music") == 0) {
harness_game_config.no_music = 1;
handled = 1;
} else if (strcasecmp(argv[i], "--game-completed") == 0) {
harness_game_config.game_completed = 1;
handled = 1;
}
if (handled) {
@ -291,8 +306,7 @@ FILE* Harness_Hook_fopen(const char* pathname, const char* mode) {
}
// Localization
int Harness_Hook_isalnum(int c)
{
int Harness_Hook_isalnum(int c) {
if (harness_game_info.localization == eGameLocalization_polish) {
// Polish diacritic letters in Windows-1250
unsigned char letters[] = { 140, 143, 156, 159, 163, 165, 175, 179, 185, 191, 198, 202, 209, 211, 230, 234, 241, 243 };

View File

@ -31,6 +31,7 @@ typedef struct tHarness_game_info {
// built-in shifted keyboard look-up table for certain localized Carmageddon releases
int* ascii_shift_table;
} defines;
int data_dir_has_3dfx_assets;
} tHarness_game_info;
typedef struct tHarness_game_config {
@ -47,6 +48,8 @@ typedef struct tHarness_game_config {
int no_bind;
int no_music;
int verbose;
int opengl_3dfx_mode;
int game_completed;
int install_signalhandler;
} tHarness_game_config;

View File

@ -5,14 +5,17 @@
#include "harness/win95_polyfill_defs.h"
#include <stdio.h>
typedef enum tHarness_window_type {
eWindow_type_software = 0,
eWindow_type_opengl = 1,
} tHarness_window_type;
// Platform implementation functions
typedef struct tHarness_platform {
// Render a fullscreen quad using the specified pixel data
void (*Renderer_Present)(br_pixelmap* src);
// Set the 256 color palette to use (BGRA format)
void (*Renderer_SetPalette)(PALETTEENTRY_* palette);
// Create a window. Return a handle to the window
void* (*CreateWindowAndRenderer)(char* title, int x, int y, int nWidth, int nHeight);
// Get mouse button state
int (*GetMouseButtons)(int* button_1, int* button_2);
// Get mouse position
@ -20,7 +23,7 @@ typedef struct tHarness_platform {
// Close specified window
void (*DestroyWindow)(void* window);
// Process window messages, return any WM_QUIT message
int (*ProcessWindowMessages)(MSG_* msg);
void (*ProcessWindowMessages)(MSG_* msg);
// Set position of a window
int (*SetWindowPos)(void* hWnd, int x, int y, int nWidth, int nHeight);
// Show/hide the cursor
@ -34,6 +37,14 @@ typedef struct tHarness_platform {
// Show error message
int (*ShowErrorMessage)(void* window, char* text, char* caption);
// Create a window. Uses an underscore to avoid name collisions with windows.h `CreateWindow` macro
void (*CreateWindow_)(char* title, int nWidth, int nHeight, tHarness_window_type window_type);
void (*Swap)(br_pixelmap* back_buffer);
void (*PaletteChanged)(br_colour entries[256]);
// If this platform supports OpenGL
void* (*GL_GetProcAddress)(const char* name);
void (*GetViewport)(int* x, int* y, float* width_multiplier, float* height_multiplier);
} tHarness_platform;
extern tHarness_platform gHarness_platform;

View File

@ -26,6 +26,10 @@
// Optional: install a handler to print stack trace during a crash
void OS_InstallSignalHandler(char* program_name);
char* OS_GetFirstFileInDirectory(char* path);
char* OS_GetNextFileInDirectory(void);
FILE* OS_fopen(const char* pathname, const char* mode);
size_t OS_ConsoleReadPassword(char* pBuffer, size_t pBufferLen);

View File

@ -34,6 +34,7 @@ static char _program_name[1024];
static void* stack_traces[MAX_STACK_FRAMES];
static char name_buf[4096];
static DIR* directory_iterator;
struct dl_iterate_callback_data {
int initialized;
@ -236,6 +237,30 @@ void OS_InstallSignalHandler(char* program_name) {
}
}
char* OS_GetFirstFileInDirectory(char* path) {
directory_iterator = opendir(path);
if (directory_iterator == NULL) {
return NULL;
}
return OS_GetNextFileInDirectory();
}
char* OS_GetNextFileInDirectory(void) {
struct dirent* entry;
if (directory_iterator == NULL) {
return NULL;
}
while ((entry = readdir(directory_iterator)) != NULL) {
if (entry->d_type == DT_REG) {
return entry->d_name;
}
}
closedir(directory_iterator);
directory_iterator = NULL;
return NULL;
}
FILE* OS_fopen(const char* pathname, const char* mode) {
FILE* f = fopen(pathname, mode);
if (f != NULL) {

View File

@ -29,6 +29,7 @@ static char _program_name[1024];
#define MAX_STACK_FRAMES 64
static void* stack_traces[MAX_STACK_FRAMES];
static char name_buf[4096];
static DIR* directory_iterator;
// Resolve symbol name and source location given the path to the executable and an address
int addr2line(char const* const program_name, intptr_t slide, void const* const addr) {
@ -220,6 +221,30 @@ void OS_InstallSignalHandler(char* program_name) {
}
}
char* OS_GetFirstFileInDirectory(char* path) {
directory_iterator = opendir(path);
if (directory_iterator == NULL) {
return NULL;
}
return OS_GetNextFileInDirectory();
}
char* OS_GetNextFileInDirectory(void) {
struct dirent* entry;
if (directory_iterator == NULL) {
return NULL;
}
while ((entry = readdir(directory_iterator)) != NULL) {
if (entry->d_type == DT_REG) {
return entry->d_name;
}
}
closedir(directory_iterator);
directory_iterator = NULL;
return NULL;
}
FILE* OS_fopen(const char* pathname, const char* mode) {
FILE* f;

View File

@ -2,6 +2,8 @@
// this has to be first
#include <windows.h>
//
#include <dbghelp.h>
#include "harness/config.h"
@ -9,9 +11,9 @@
#include "harness/trace.h"
#include <errno.h> /* errno, strerror */
#include <io.h> /* _access_s, F_OK */
#include <io.h> /* _access_s, F_OK */
#include <stddef.h>
#include <stdio.h> /* errno_t, FILE, fgets, fopen_s, fprintf*/
#include <stdio.h> /* errno_t, FILE, fgets, fopen_s, fprintf*/
#include <stdlib.h> /* _splitpath */
#include <string.h> /* strcpy, strerror, strlen, strrchr */
@ -28,7 +30,10 @@ static char path_addr2line[1024];
static char dirname_buf[_MAX_DIR];
static char fname_buf[_MAX_FNAME];
#if defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) ||defined( __i386) || defined(_M_IX86)
HANDLE directory_handle = NULL;
char last_found_file[260];
#if defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(__i386) || defined(_M_IX86)
#define DETHRACE_CPU_X86 1
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
#define DETHRACE_CPU_X64 1
@ -44,7 +49,7 @@ static char fname_buf[_MAX_FNAME];
static BOOL print_addr2line_address_location(HANDLE const hProcess, const DWORD64 address) {
char addr2line_cmd[1024] = { 0 };
const char *program_name = windows_program_name;
const char* program_name = windows_program_name;
IMAGEHLP_MODULE64 module_info;
if (path_addr2line[0] == '\0') {
@ -63,7 +68,7 @@ static BOOL print_addr2line_address_location(HANDLE const hProcess, const DWORD6
return TRUE;
}
static void printf_windows_message(const char *format, ...) {
static void printf_windows_message(const char* format, ...) {
va_list ap;
char win_msg[512];
FormatMessageA(
@ -71,11 +76,11 @@ static void printf_windows_message(const char *format, ...) {
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
win_msg, sizeof(win_msg)/sizeof(*win_msg),
win_msg, sizeof(win_msg) / sizeof(*win_msg),
NULL);
size_t win_msg_len = strlen(win_msg);
while (win_msg[win_msg_len-1] == '\r' || win_msg[win_msg_len-1] == '\n' || win_msg[win_msg_len-1] == ' ') {
win_msg[win_msg_len-1] = '\0';
while (win_msg[win_msg_len - 1] == '\r' || win_msg[win_msg_len - 1] == '\n' || win_msg[win_msg_len - 1] == ' ') {
win_msg[win_msg_len - 1] = '\0';
win_msg_len--;
}
va_start(ap, format);
@ -109,9 +114,9 @@ static BOOL print_dbghelp_address_location(HANDLE const hProcess, const DWORD64
DWORD64 dwDisplacement;
DWORD lineColumn = 0;
IMAGEHLP_LINE64 line;
const char *image_file_name;
const char *symbol_name;
const char *file_name;
const char* image_file_name;
const char* symbol_name;
const char* file_name;
char line_number[16];
memset(&module_info, 0, sizeof(module_info));
@ -198,14 +203,14 @@ static void print_stacktrace(CONTEXT* context) {
#endif
while (StackWalk(machine_type,
GetCurrentProcess(),
GetCurrentThread(),
&frame,
context,
0,
SymFunctionTableAccess,
SymGetModuleBase,
0)) {
GetCurrentProcess(),
GetCurrentThread(),
&frame,
context,
0,
SymFunctionTableAccess,
SymGetModuleBase,
0)) {
if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
fprintf(stderr, "PC == Return Address => Possible endless callstack\n");
@ -306,7 +311,7 @@ static LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS* ExceptionInfo)
}
void OS_InstallSignalHandler(char* program_name) {
const char *env_addr2line;
const char* env_addr2line;
path_addr2line[0] = '\0';
env_addr2line = getenv("ADDR2LINE");
@ -322,6 +327,35 @@ void OS_InstallSignalHandler(char* program_name) {
SetUnhandledExceptionFilter(windows_exception_handler);
}
char* OS_GetFirstFileInDirectory(char* path) {
char with_extension[256];
WIN32_FIND_DATA find_data;
strcpy(with_extension, path);
strcat(with_extension, "\\*.???");
directory_handle = FindFirstFile(with_extension, &find_data);
if (directory_handle == INVALID_HANDLE_VALUE) {
return NULL;
}
strcpy(last_found_file, find_data.cFileName);
return last_found_file;
}
// Required: continue directory iteration. If no more files, return NULL
char* OS_GetNextFileInDirectory(void) {
WIN32_FIND_DATA find_data;
if (directory_handle == NULL) {
return NULL;
}
while (FindNextFile(directory_handle, &find_data)) {
strcpy(last_found_file, find_data.cFileName);
return last_found_file;
}
FindClose(directory_handle);
return NULL;
}
FILE* OS_fopen(const char* pathname, const char* mode) {
FILE* f;
errno_t err;

View File

@ -3,10 +3,6 @@
// todo: shouldnt depend on sdl...
#include <SDL.h>
static void* null_create_window_and_renderer(char* title, int x, int y, int width, int height) {
return 0;
}
static int null_set_window_pos(void* hWnd, int x, int y, int nWidth, int nHeight) {
return 0;
}
@ -18,8 +14,7 @@ static int null_show_error_message(void* window, char* text, char* caption) {
return 0;
}
static int null_get_and_handle_message(MSG_* msg) {
return 0;
static void null_get_and_handle_message(MSG_* msg) {
}
static void null_get_keyboard_state(unsigned int count, uint8_t* buffer) {
@ -45,7 +40,6 @@ void Null_Platform_Init(tHarness_platform* platform) {
// todo: shouldnt depend on sdl...
platform->Sleep = SDL_Delay;
platform->GetTicks = SDL_GetTicks;
platform->CreateWindowAndRenderer = null_create_window_and_renderer;
platform->ShowCursor = null_show_cursor;
platform->SetWindowPos = null_set_window_pos;
platform->DestroyWindow = null_destroy_window;

View File

@ -11,52 +11,46 @@ SDL_Renderer* renderer;
SDL_Texture* screen_texture;
uint32_t converted_palette[256];
br_pixelmap* last_screen_src;
SDL_GLContext* gl_context;
int render_width, render_height;
Uint32 last_frame_time;
uint8_t directinput_key_state[SDL_NUM_SCANCODES];
static void* create_window_and_renderer(char* title, int x, int y, int width, int height) {
render_width = width;
render_height = height;
struct {
int x, y;
float scale_x, scale_y;
} viewport;
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
LOG_PANIC("SDL_INIT_VIDEO error: %s", SDL_GetError());
}
// Callbacks back into original game code
extern void QuitGame(void);
extern uint32_t gKeyboard_bits[8];
extern br_pixelmap* gBack_screen;
window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
width, height,
SDL_WINDOW_RESIZABLE);
static void calculate_viewport(int window_width, int window_height) {
int vp_width, vp_height;
float target_aspect_ratio;
float aspect_ratio;
if (window == NULL) {
LOG_PANIC("Failed to create window: %s", SDL_GetError());
}
aspect_ratio = (float)window_width / window_height;
target_aspect_ratio = (float)gBack_screen->width / gBack_screen->height;
if (harness_game_config.start_full_screen) {
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
if (renderer == NULL) {
LOG_PANIC("Failed to create renderer: %s", SDL_GetError());
}
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
SDL_RenderSetLogicalSize(renderer, render_width, render_height);
screen_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
if (screen_texture == NULL) {
SDL_RendererInfo info;
SDL_GetRendererInfo(renderer, &info);
for (Uint32 i = 0; i < info.num_texture_formats; i++) {
LOG_INFO("%s\n", SDL_GetPixelFormatName(info.texture_formats[i]));
vp_width = window_width;
vp_height = window_height;
if (aspect_ratio != target_aspect_ratio) {
if (aspect_ratio > target_aspect_ratio) {
vp_width = window_height * target_aspect_ratio + .5f;
} else {
vp_height = window_width / target_aspect_ratio + .5f;
}
LOG_PANIC("Failed to create screen_texture: %s", SDL_GetError());
}
return window;
viewport.x = (window_width - vp_width) / 2;
viewport.y = (window_height - vp_height) / 2;
viewport.scale_x = (float)vp_width / gBack_screen->width;
viewport.scale_y = (float)vp_height / gBack_screen->height;
}
static int set_window_pos(void* hWnd, int x, int y, int nWidth, int nHeight) {
@ -82,7 +76,7 @@ static int is_only_key_modifier(int modifier_flags, int flag_check) {
return (modifier_flags & flag_check) && (modifier_flags & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_GUI)) == (modifier_flags & flag_check);
}
static int get_and_handle_message(MSG_* msg) {
static void get_and_handle_message(MSG_* msg) {
SDL_Event event;
int dinput_key;
@ -97,7 +91,7 @@ static int get_and_handle_message(MSG_* msg) {
if (event.key.type == SDL_KEYDOWN) {
if ((event.key.keysym.mod & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_GUI))) {
// Ignore keydown of RETURN when used together with some modifier
return 0;
return;
}
} else if (event.key.type == SDL_KEYUP) {
if (is_only_key_modifier(event.key.keysym.mod, KMOD_ALT)) {
@ -111,28 +105,28 @@ static int get_and_handle_message(MSG_* msg) {
dinput_key = sdlScanCodeToDirectInputKeyNum[event.key.keysym.scancode];
if (dinput_key == 0) {
LOG_WARN("unexpected scan code %s (%d)", SDL_GetScancodeName(event.key.keysym.scancode), event.key.keysym.scancode);
return 0;
return;
}
// DInput expects high bit to be set if key is down
// https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ee418261(v=vs.85)
directinput_key_state[dinput_key] = (event.type == SDL_KEYDOWN ? 0x80 : 0);
if (event.type == SDL_KEYDOWN) {
gKeyboard_bits[dinput_key >> 5] |= (1 << (dinput_key & 0x1F));
} else {
gKeyboard_bits[dinput_key >> 5] &= ~(1 << (dinput_key & 0x1F));
}
break;
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_CLOSE) {
if (SDL_GetWindowID(window) == event.window.windowID) {
msg->message = WM_QUIT;
return 1;
}
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
calculate_viewport(event.window.data1, event.window.data2);
}
break;
case SDL_QUIT:
msg->message = WM_QUIT;
return 1;
QuitGame();
}
}
return 0;
}
static void get_keyboard_state(unsigned int count, uint8_t* buffer) {
@ -152,21 +146,24 @@ static int get_mouse_buttons(int* pButton1, int* pButton2) {
}
static int get_mouse_position(int* pX, int* pY) {
int window_width, window_height;
float lX, lY;
if (SDL_GetMouseFocus() != window) {
return 0;
}
SDL_GetMouseState(pX, pY);
SDL_RenderWindowToLogical(renderer, *pX, *pY, &lX, &lY);
SDL_GetWindowSize(window, &window_width, &window_height);
#if defined(DETHRACE_FIX_BUGS)
// In hires mode (640x480), the menus are still rendered at (320x240),
// so prescale the cursor coordinates accordingly.
lX *= 320;
lX /= render_width;
lY *= 200;
lY /= render_height;
#endif
SDL_GetMouseState(pX, pY);
if (renderer != NULL) {
// software renderer
SDL_RenderWindowToLogical(renderer, *pX, *pY, &lX, &lY);
} else {
// hardware renderer
// handle case where window is stretched larger than the pixel size
lX = *pX * (640.0f / window_width);
lY = *pY * (480.0f / window_height);
}
*pX = (int)lX;
*pY = (int)lY;
return 0;
@ -187,50 +184,148 @@ static void limit_fps(void) {
last_frame_time = SDL_GetTicks();
}
static void present_screen(br_pixelmap* src) {
// fastest way to convert 8 bit indexed to 32 bit
uint8_t* src_pixels = src->pixels;
uint32_t* dest_pixels;
int dest_pitch;
SDL_LockTexture(screen_texture, NULL, (void**)&dest_pixels, &dest_pitch);
for (int i = 0; i < src->height * src->width; i++) {
*dest_pixels = converted_palette[*src_pixels];
dest_pixels++;
src_pixels++;
}
SDL_UnlockTexture(screen_texture);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, screen_texture, NULL, NULL);
SDL_RenderPresent(renderer);
last_screen_src = src;
if (harness_game_config.fps != 0) {
limit_fps();
}
}
static void set_palette(PALETTEENTRY_* pal) {
for (int i = 0; i < 256; i++) {
converted_palette[i] = (0xff << 24 | pal[i].peRed << 16 | pal[i].peGreen << 8 | pal[i].peBlue);
}
if (last_screen_src != NULL) {
present_screen(last_screen_src);
}
}
int show_error_message(void* window, char* text, char* caption) {
fprintf(stderr, "%s", text);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, caption, text, window);
return 0;
}
static void create_window(char* title, int width, int height, tHarness_window_type window_type) {
int window_width, window_height;
render_width = width;
render_height = height;
window_width = width;
window_height = height;
// special case lores and make a bigger window
if (width == 320 && height == 200) {
window_width = 640;
window_height = 480;
}
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
LOG_PANIC("SDL_INIT_VIDEO error: %s", SDL_GetError());
}
if (window_type == eWindow_type_opengl) {
window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
window_width, window_height,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (window == NULL) {
LOG_PANIC("Failed to create window: %s", SDL_GetError());
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
gl_context = SDL_GL_CreateContext(window);
if (gl_context == NULL) {
LOG_WARN("Failed to create OpenGL core profile: %s. Trying OpenGLES...", SDL_GetError());
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
gl_context = SDL_GL_CreateContext(window);
}
if (gl_context == NULL) {
LOG_PANIC("Failed to create OpenGL context: %s", SDL_GetError());
}
SDL_GL_SetSwapInterval(1);
} else {
window = SDL_CreateWindow(title,
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
window_width, window_height,
SDL_WINDOW_RESIZABLE);
if (window == NULL) {
LOG_PANIC("Failed to create window: %s", SDL_GetError());
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
if (renderer == NULL) {
LOG_PANIC("Failed to create renderer: %s", SDL_GetError());
}
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
SDL_RenderSetLogicalSize(renderer, render_width, render_height);
screen_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
if (screen_texture == NULL) {
SDL_RendererInfo info;
SDL_GetRendererInfo(renderer, &info);
for (Uint32 i = 0; i < info.num_texture_formats; i++) {
LOG_INFO("%s\n", SDL_GetPixelFormatName(info.texture_formats[i]));
}
LOG_PANIC("Failed to create screen_texture: %s", SDL_GetError());
}
}
SDL_ShowCursor(SDL_DISABLE);
viewport.x = 0;
viewport.y = 0;
viewport.scale_x = 1;
viewport.scale_y = 1;
if (harness_game_config.start_full_screen) {
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
}
}
static void swap(br_pixelmap* back_buffer) {
uint8_t* src_pixels = back_buffer->pixels;
uint32_t* dest_pixels;
int dest_pitch;
get_and_handle_message(NULL);
if (gl_context != NULL) {
SDL_GL_SwapWindow(window);
} else {
SDL_LockTexture(screen_texture, NULL, (void**)&dest_pixels, &dest_pitch);
for (int i = 0; i < back_buffer->height * back_buffer->width; i++) {
*dest_pixels = converted_palette[*src_pixels];
dest_pixels++;
src_pixels++;
}
SDL_UnlockTexture(screen_texture);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, screen_texture, NULL, NULL);
SDL_RenderPresent(renderer);
last_screen_src = back_buffer;
}
if (harness_game_config.fps != 0) {
limit_fps();
}
}
static void palette_changed(br_colour entries[256]) {
for (int i = 0; i < 256; i++) {
converted_palette[i] = (0xff << 24 | BR_RED(entries[i]) << 16 | BR_GRN(entries[i]) << 8 | BR_BLU(entries[i]));
}
if (last_screen_src != NULL) {
swap(last_screen_src);
}
}
static void get_viewport(int* x, int* y, float* width_multipler, float* height_multiplier) {
*x = viewport.x;
*y = viewport.y;
*width_multipler = viewport.scale_x;
*height_multiplier = viewport.scale_y;
}
void Harness_Platform_Init(tHarness_platform* platform) {
platform->ProcessWindowMessages = get_and_handle_message;
platform->Sleep = SDL_Delay;
platform->GetTicks = SDL_GetTicks;
platform->CreateWindowAndRenderer = create_window_and_renderer;
platform->ShowCursor = SDL_ShowCursor;
platform->SetWindowPos = set_window_pos;
platform->DestroyWindow = destroy_window;
@ -238,6 +333,10 @@ void Harness_Platform_Init(tHarness_platform* platform) {
platform->GetMousePosition = get_mouse_position;
platform->GetMouseButtons = get_mouse_buttons;
platform->ShowErrorMessage = show_error_message;
platform->Renderer_SetPalette = set_palette;
platform->Renderer_Present = present_screen;
platform->CreateWindow_ = create_window;
platform->Swap = swap;
platform->PaletteChanged = palette_changed;
platform->GL_GetProcAddress = SDL_GL_GetProcAddress;
platform->GetViewport = get_viewport;
}

View File

@ -1,4 +1,4 @@
#if 0
#include "harness/hooks.h"
#include "harness/os.h"
#include "harness/win95_polyfill.h"
@ -172,7 +172,8 @@ int FindClose_(HANDLE_ hFindFile) {
}
void* CreateWindowExA_(uint32_t dwExStyle, char* lpClassName, char* lpWindowName, uint32_t dwStyle, int X, int Y, int nWidth, int nHeight, void* hWndParent, void* hMenu, void* hInstance, void* lpParam) {
return gHarness_platform.CreateWindowAndRenderer(lpWindowName, X, Y, nWidth, nHeight);
gHarness_platform.CreateWindow_(lpWindowName, nWidth, nHeight, eWindow_type_software);
return NULL;
}
int SetWindowPos_(void* hWnd, void* hWndInsertAfter, int X, int Y, int cx, int cy, unsigned int uFlags) {
@ -249,3 +250,5 @@ int _CrtDbgReport_(int reportType, const char* filename, int linenumber, const c
printf("_CrtDbgReport: (TODO)\n");
return 1;
}
#endif

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import argparse
import sys