From ca1ef76e7f2beef062394b2b16c2d5149c668c5f Mon Sep 17 00:00:00 2001 From: Dethrace Engineering Department <78985374+dethrace-labs@users.noreply.github.com> Date: Mon, 31 Mar 2025 18:44:38 +1300 Subject: [PATCH] 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 --- .github/workflows/workflow.yaml | 7 +- CMakeLists.txt | 2 + README.md | 6 +- lib/BRender-v1.3.2 | 2 +- src/DETHRACE/CMakeLists.txt | 3 + src/DETHRACE/common/car.c | 7 +- src/DETHRACE/common/depth.c | 88 +++- src/DETHRACE/common/displays.c | 37 +- src/DETHRACE/common/errors.c | 12 + src/DETHRACE/common/flicplay.c | 11 +- src/DETHRACE/common/globvrbm.c | 3 + src/DETHRACE/common/globvrbm.h | 2 + src/DETHRACE/common/graphics.c | 309 +++++++++++++- src/DETHRACE/common/init.c | 254 ++++++++++- src/DETHRACE/common/loading.c | 110 ++++- src/DETHRACE/common/main.c | 20 + src/DETHRACE/common/main.h | 3 +- src/DETHRACE/common/mainloop.c | 8 + src/DETHRACE/common/mainmenu.c | 2 + src/DETHRACE/common/netgame.c | 2 + src/DETHRACE/common/network.c | 6 +- src/DETHRACE/common/oil.c | 3 + src/DETHRACE/common/pratcam.c | 93 +++- src/DETHRACE/common/racestrt.c | 22 +- src/DETHRACE/common/racesumm.c | 211 ++++++--- src/DETHRACE/common/skidmark.c | 19 +- src/DETHRACE/common/spark.c | 309 ++++++++++---- src/DETHRACE/common/spark.h | 2 +- src/DETHRACE/common/structur.c | 2 + src/DETHRACE/common/utility.c | 366 +++++++++++++++- src/DETHRACE/common/world.c | 257 ++++++++--- src/DETHRACE/constants.h | 10 + src/DETHRACE/main.c | 5 +- src/DETHRACE/pc-dos/dossys.c | 616 +++++++++++++++++++++++---- src/DETHRACE/pc-dos/scancodes.h | 106 +++++ src/DETHRACE/pc-win95/ssdx.c | 3 + src/DETHRACE/pc-win95/win95sys.c | 36 +- src/DETHRACE/pd/sys.h | 42 +- src/harness/harness.c | 24 +- src/harness/include/harness/config.h | 3 + src/harness/include/harness/hooks.h | 17 +- src/harness/include/harness/os.h | 4 + src/harness/os/linux.c | 25 ++ src/harness/os/macos.c | 25 ++ src/harness/os/windows.c | 74 +++- src/harness/platforms/null.c | 8 +- src/harness/platforms/sdl2.c | 281 ++++++++---- src/harness/win95/polyfill.c | 7 +- tools/decode_datatxt.py | 2 +- 49 files changed, 2926 insertions(+), 540 deletions(-) create mode 100644 src/DETHRACE/pc-dos/scancodes.h diff --git a/.github/workflows/workflow.yaml b/.github/workflows/workflow.yaml index da73ba6c..e8bcd024 100644 --- a/.github/workflows/workflow.yaml +++ b/.github/workflows/workflow.yaml @@ -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: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 762e138f..472375dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 "$<$:${FLAG}>") endif() endfunction() diff --git a/README.md b/README.md index 0ceb47c2..3ec49ace 100644 --- a/README.md +++ b/README.md @@ -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: Screenshot 2024-05-27 at 8 44 10 AM diff --git a/lib/BRender-v1.3.2 b/lib/BRender-v1.3.2 index 9c340863..ee344356 160000 --- a/lib/BRender-v1.3.2 +++ b/lib/BRender-v1.3.2 @@ -1 +1 @@ -Subproject commit 9c34086300f4f0bbb3a55206380f25b17dad6c12 +Subproject commit ee344356e359d9b9b310dda08e3be672da9b7f01 diff --git a/src/DETHRACE/CMakeLists.txt b/src/DETHRACE/CMakeLists.txt index f44e5bd7..770f10d8 100644 --- a/src/DETHRACE/CMakeLists.txt +++ b/src/DETHRACE/CMakeLists.txt @@ -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) diff --git a/src/DETHRACE/common/car.c b/src/DETHRACE/common/car.c index 35e8914a..2ad0ad8b 100644 --- a/src/DETHRACE/common/car.c +++ b/src/DETHRACE/common/car.c @@ -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; } diff --git a/src/DETHRACE/common/depth.c b/src/DETHRACE/common/depth.c index 28fa518f..ce2eb490 100644 --- a/src/DETHRACE/common/depth.c +++ b/src/DETHRACE/common/depth.c @@ -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@(int pShift@) 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@) @@ -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@, br_pixelmap *pSky_texture@, int pStart@, int pEnd@) @@ -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); } diff --git a/src/DETHRACE/common/displays.c b/src/DETHRACE/common/displays.c index 6620c5cd..c84243b5 100644 --- a/src/DETHRACE/common/displays.c +++ b/src/DETHRACE/common/displays.c @@ -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@, int pLeft@, int pTop@, int pRight@, 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], diff --git a/src/DETHRACE/common/errors.c b/src/DETHRACE/common/errors.c index 7d95a792..83063187 100644 --- a/src/DETHRACE/common/errors.c +++ b/src/DETHRACE/common/errors.c @@ -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 diff --git a/src/DETHRACE/common/flicplay.c b/src/DETHRACE/common/flicplay.c index f527c531..343f43a4 100644 --- a/src/DETHRACE/common/flicplay.c +++ b/src/DETHRACE/common/flicplay.c @@ -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@) diff --git a/src/DETHRACE/common/globvrbm.c b/src/DETHRACE/common/globvrbm.c index caefdf66..08558139 100644 --- a/src/DETHRACE/common/globvrbm.c +++ b/src/DETHRACE/common/globvrbm.c @@ -34,3 +34,6 @@ int gMax_texture_side; int gDevious_2d; int gMax_texture_aspect_ratio; int gMaterial_fogging; + +// Added +int gVoodoo_rush_mode; diff --git a/src/DETHRACE/common/globvrbm.h b/src/DETHRACE/common/globvrbm.h index 4a01ad58..a3677a94 100644 --- a/src/DETHRACE/common/globvrbm.h +++ b/src/DETHRACE/common/globvrbm.h @@ -37,4 +37,6 @@ extern int gDevious_2d; extern int gMax_texture_aspect_ratio; extern int gMaterial_fogging; +extern int gVoodoo_rush_mode; + #endif diff --git a/src/DETHRACE/common/graphics.c b/src/DETHRACE/common/graphics.c index 38b6b03a..341b8864 100644 --- a/src/DETHRACE/common/graphics.c +++ b/src/DETHRACE/common/graphics.c @@ -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@, br_int_16 pDest_x@, br_int_16 pOffset_x@, br_int_16 pDest_y@, 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@, br_int_16 pDest_x@, br_int_16 pOffset_x@, br_int_16 pDest_y@, 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@, br_actor *pWorld@, tTrack_spec *pTrack_spec@, br_actor *pCamera@, 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)); diff --git a/src/DETHRACE/common/init.c b/src/DETHRACE/common/init.c index 711220cd..8cd241a6 100644 --- a/src/DETHRACE/common/init.c +++ b/src/DETHRACE/common/init.c @@ -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@, char **pArgv@) @@ -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@, char **pArgv@) 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@) @@ -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() diff --git a/src/DETHRACE/common/loading.c b/src/DETHRACE/common/loading.c index bc9d42db..84472ccc 100644 --- a/src/DETHRACE/common/loading.c +++ b/src/DETHRACE/common/loading.c @@ -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@(char *pName@) @@ -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@(char *pName@) @@ -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@(br_pixelmap *pThe_br_map@) @@ -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); diff --git a/src/DETHRACE/common/main.c b/src/DETHRACE/common/main.c index 76843c8f..a94fccc7 100644 --- a/src/DETHRACE/common/main.c +++ b/src/DETHRACE/common/main.c @@ -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); diff --git a/src/DETHRACE/common/main.h b/src/DETHRACE/common/main.h index b74d6b4b..a9693d65 100644 --- a/src/DETHRACE/common/main.h +++ b/src/DETHRACE/common/main.h @@ -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); diff --git a/src/DETHRACE/common/mainloop.c b/src/DETHRACE/common/mainloop.c index 858375fd..2ffc000d 100644 --- a/src/DETHRACE/common/mainloop.c +++ b/src/DETHRACE/common/mainloop.c @@ -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(); diff --git a/src/DETHRACE/common/mainmenu.c b/src/DETHRACE/common/mainmenu.c index 037daa4a..f4993413 100644 --- a/src/DETHRACE/common/mainmenu.c +++ b/src/DETHRACE/common/mainmenu.c @@ -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(); } diff --git a/src/DETHRACE/common/netgame.c b/src/DETHRACE/common/netgame.c index f7b166a6..0ac2c719 100644 --- a/src/DETHRACE/common/netgame.c +++ b/src/DETHRACE/common/netgame.c @@ -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); diff --git a/src/DETHRACE/common/network.c b/src/DETHRACE/common/network.c index 0736dc7a..e859e963 100644 --- a/src/DETHRACE/common/network.c +++ b/src/DETHRACE/common/network.c @@ -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@) void ReceivedDisableCar(tNet_contents* pContents) { LOG_TRACE("(%p)", pContents); - } // IDA: void __usercall ReceivedEnableCar(tNet_contents *pContents@) void ReceivedEnableCar(tNet_contents* pContents) { LOG_TRACE("(%p)", pContents); - } // IDA: void __usercall ReceivedScores(tNet_contents *pContents@) diff --git a/src/DETHRACE/common/oil.c b/src/DETHRACE/common/oil.c index e31c16b8..d0cae192 100644 --- a/src/DETHRACE/common/oil.c +++ b/src/DETHRACE/common/oil.c @@ -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; diff --git a/src/DETHRACE/common/pratcam.c b/src/DETHRACE/common/pratcam.c index 051ed6be..74536d2e 100644 --- a/src/DETHRACE/common/pratcam.c +++ b/src/DETHRACE/common/pratcam.c @@ -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( diff --git a/src/DETHRACE/common/racestrt.c b/src/DETHRACE/common/racestrt.c index b07378b2..e2d14230 100644 --- a/src/DETHRACE/common/racestrt.c +++ b/src/DETHRACE/common/racestrt.c @@ -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"); diff --git a/src/DETHRACE/common/racesumm.c b/src/DETHRACE/common/racesumm.c index 1de6947f..2d7175c0 100644 --- a/src/DETHRACE/common/racesumm.c +++ b/src/DETHRACE/common/racesumm.c @@ -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@(br_matrix34 *pMat@) @@ -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@(int *pCurrent_choice@, int *pCurrent_mode@) @@ -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); diff --git a/src/DETHRACE/common/skidmark.c b/src/DETHRACE/common/skidmark.c index 025a3b6a..e520363d 100644 --- a/src/DETHRACE/common/skidmark.c +++ b/src/DETHRACE/common/skidmark.c @@ -6,6 +6,7 @@ #include "loading.h" #include "oil.h" #include "piping.h" +#include "utility.h" #include #include #include @@ -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++) { diff --git a/src/DETHRACE/common/spark.c b/src/DETHRACE/common/spark.c index 55ac7a8f..aa0c9e25 100644 --- a/src/DETHRACE/common/spark.c +++ b/src/DETHRACE/common/spark.c @@ -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@, br_vector3 *pEnd@) 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@(br_vector3 *start@, br_vector3 *end@, br_pixelmap *pScreen@, br_pixelmap *pDepth_buffer@, 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@) 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@, br_pixelmap *pDepth_buffer@, br_actor *pCamera@, tU32 pTime@) @@ -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@, br_vector3 *pPos@, br_vector3 *pVel@) @@ -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@, 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@, 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@, br_vector3 *v@, br_scalar strength, br_scalar pDecay_factor, int pType, tCar_spec *pC) diff --git a/src/DETHRACE/common/spark.h b/src/DETHRACE/common/spark.h index 71bb16d1..cb37027a 100644 --- a/src/DETHRACE/common/spark.h +++ b/src/DETHRACE/common/spark.h @@ -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); diff --git a/src/DETHRACE/common/structur.c b/src/DETHRACE/common/structur.c index 75df0232..b4a3fbce 100644 --- a/src/DETHRACE/common/structur.c +++ b/src/DETHRACE/common/structur.c @@ -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() diff --git a/src/DETHRACE/common/utility.c b/src/DETHRACE/common/utility.c index f4c31aa5..df46529c 100644 --- a/src/DETHRACE/common/utility.c +++ b/src/DETHRACE/common/utility.c @@ -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@(br_pixelmap *pSrc@, int pWidth@, int pHeight@) @@ -447,7 +468,14 @@ br_pixelmap* Tile8BitPixelmap(br_pixelmap* pSrc, int pN) { // IDA: tException_list __usercall FindExceptionInList@(char *pName@, tException_list pList@) 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@(br_pixelmap *pSrc@) @@ -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@(char *pFile_name@) @@ -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@(br_pixelmap *pSrc@) @@ -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@, br_pixelmap *pSrc@, br_pixelmap *pPalette@) @@ -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@, tS16 pDst_x@, tS16 pDst_y@, br_pixelmap *pSrc@, 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@, tS16 pDst_x@, tS16 pDst_y@, br_pixelmap *pSrc@, 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@, tS16 pDst_x@, tS16 pDst_y@, br_pixelmap *pSrc@, 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@, tS16 pDst_x@, tS16 pDst_y@, br_pixelmap *pSrc@, 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@, 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) 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@, br_pixelmap *src@) 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@, br_int_16 x@, br_int_16 y@, br_uint_16 w@, 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@, int pN@) @@ -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@) @@ -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@(char *pThe_path@) diff --git a/src/DETHRACE/common/world.c b/src/DETHRACE/common/world.c index 605c1f64..ddd646ce 100644 --- a/src/DETHRACE/common/world.c +++ b/src/DETHRACE/common/world.c @@ -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@, tPMFMCB pCallback@) @@ -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@, int pOwner@, int pRef_offset@) @@ -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@, tException_list pNew@) 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@) @@ -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@) 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@, tTrack_spec *pTrack_spec@, tRace_info *pRace_info@) @@ -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() diff --git a/src/DETHRACE/constants.h b/src/DETHRACE/constants.h index a0252020..0f6185dd 100644 --- a/src/DETHRACE/constants.h +++ b/src/DETHRACE/constants.h @@ -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 diff --git a/src/DETHRACE/main.c b/src/DETHRACE/main.c index 626b1f60..df920171 100644 --- a/src/DETHRACE/main.c +++ b/src/DETHRACE/main.c @@ -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) { diff --git a/src/DETHRACE/pc-dos/dossys.c b/src/DETHRACE/pc-dos/dossys.c index 7113b391..9720aec0 100644 --- a/src/DETHRACE/pc-dos/dossys.c +++ b/src/DETHRACE/pc-dos/dossys.c @@ -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 #include -#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@(tU8 pScan_code@) int KeyDown(tU8 pScan_code) { - NOT_IMPLEMENTED(); + return (gKeyboard_bits[pScan_code >> 5] >> (pScan_code & 0x1F)) & 1; } // IDA: void __usercall KeyTranslation(tU8 pKey_index@, tU8 pScan_code_1@, tU8 pScan_code_2@) @@ -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@(int pKey_index@) 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@, int pMark@) @@ -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@) @@ -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@) 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@) +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@) +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@, br_pixelmap *pSrc@, br_pixelmap *pPalette@) @@ -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@, br_pixelmap *pSrc@, br_pixelmap *pPalette@, tU16 pOff@, 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@, int pClear_top_and_bottom@) 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@) 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@) 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@) @@ -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@, int *pButton_2@) @@ -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@, int *pY_coord@) @@ -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@(tU32 pTime_since_last_call@) 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@) void PDDisposeActionReplayBuffer(char* pBuffer) { LOG_TRACE("(\"%s\")", pBuffer); + + free(pBuffer); } // IDA: void __usercall Usage(char *pProgpath@) 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@(int pBit@) @@ -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@(char *pThe_path@, char *pFile_name@, tU32 pMin_size@) 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; diff --git a/src/DETHRACE/pc-dos/scancodes.h b/src/DETHRACE/pc-dos/scancodes.h new file mode 100644 index 00000000..4e48ebdf --- /dev/null +++ b/src/DETHRACE/pc-dos/scancodes.h @@ -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 diff --git a/src/DETHRACE/pc-win95/ssdx.c b/src/DETHRACE/pc-win95/ssdx.c index 4324d234..4d0bba14 100644 --- a/src/DETHRACE/pc-win95/ssdx.c +++ b/src/DETHRACE/pc-win95/ssdx.c @@ -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 diff --git a/src/DETHRACE/pc-win95/win95sys.c b/src/DETHRACE/pc-win95/win95sys.c index 8c2cb40c..b807ff57 100644 --- a/src/DETHRACE/pc-win95/win95sys.c +++ b/src/DETHRACE/pc-win95/win95sys.c @@ -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 diff --git a/src/DETHRACE/pd/sys.h b/src/DETHRACE/pd/sys.h index b3976e53..104ba46c 100644 --- a/src/DETHRACE/pd/sys.h +++ b/src/DETHRACE/pd/sys.h @@ -16,11 +16,15 @@ #include #include -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); diff --git a/src/harness/harness.c b/src/harness/harness.c index 0923cd11..b223f201 100644 --- a/src/harness/harness.c +++ b/src/harness/harness.c @@ -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 }; diff --git a/src/harness/include/harness/config.h b/src/harness/include/harness/config.h index 07de9b51..97bad493 100644 --- a/src/harness/include/harness/config.h +++ b/src/harness/include/harness/config.h @@ -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; diff --git a/src/harness/include/harness/hooks.h b/src/harness/include/harness/hooks.h index 63ae1a9d..e529a58b 100644 --- a/src/harness/include/harness/hooks.h +++ b/src/harness/include/harness/hooks.h @@ -5,14 +5,17 @@ #include "harness/win95_polyfill_defs.h" #include +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; diff --git a/src/harness/include/harness/os.h b/src/harness/include/harness/os.h index 404c36ce..8c047e6e 100644 --- a/src/harness/include/harness/os.h +++ b/src/harness/include/harness/os.h @@ -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); diff --git a/src/harness/os/linux.c b/src/harness/os/linux.c index 0c1b0445..8199293a 100644 --- a/src/harness/os/linux.c +++ b/src/harness/os/linux.c @@ -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) { diff --git a/src/harness/os/macos.c b/src/harness/os/macos.c index c6cd7f2d..2a81fa57 100644 --- a/src/harness/os/macos.c +++ b/src/harness/os/macos.c @@ -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; diff --git a/src/harness/os/windows.c b/src/harness/os/windows.c index 2610691a..d9bf916e 100644 --- a/src/harness/os/windows.c +++ b/src/harness/os/windows.c @@ -2,6 +2,8 @@ // this has to be first #include +// + #include #include "harness/config.h" @@ -9,9 +11,9 @@ #include "harness/trace.h" #include /* errno, strerror */ -#include /* _access_s, F_OK */ +#include /* _access_s, F_OK */ #include -#include /* errno_t, FILE, fgets, fopen_s, fprintf*/ +#include /* errno_t, FILE, fgets, fopen_s, fprintf*/ #include /* _splitpath */ #include /* 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; diff --git a/src/harness/platforms/null.c b/src/harness/platforms/null.c index 54cb8231..62254672 100644 --- a/src/harness/platforms/null.c +++ b/src/harness/platforms/null.c @@ -3,10 +3,6 @@ // todo: shouldnt depend on sdl... #include -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; diff --git a/src/harness/platforms/sdl2.c b/src/harness/platforms/sdl2.c index dd80a996..01468cd1 100644 --- a/src/harness/platforms/sdl2.c +++ b/src/harness/platforms/sdl2.c @@ -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; } diff --git a/src/harness/win95/polyfill.c b/src/harness/win95/polyfill.c index f6c029a3..53bbc402 100644 --- a/src/harness/win95/polyfill.c +++ b/src/harness/win95/polyfill.c @@ -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 diff --git a/tools/decode_datatxt.py b/tools/decode_datatxt.py index bedd2b0f..f195a14b 100755 --- a/tools/decode_datatxt.py +++ b/tools/decode_datatxt.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import argparse import sys