diff --git a/src/BRSRC13/include/brender/brender.h b/src/BRSRC13/include/brender/brender.h index 325d273d..0dcf0585 100644 --- a/src/BRSRC13/include/brender/brender.h +++ b/src/BRSRC13/include/brender/brender.h @@ -22,6 +22,9 @@ br_uint_32 BrActorEnum(br_actor* parent, br_actor_enum_cbfn* callback, void* arg br_bounds* BrActorToBounds(br_bounds* b, br_actor* ap); br_uint_16 BrActorToActorMatrix34(br_matrix34* m, br_actor* a, br_actor* b); +void BrLightEnable(br_actor* l); +void BrLightDisable(br_actor* l); + // BrAllocator br_allocator* BrAllocatorSet(br_allocator* newal); diff --git a/src/DETHRACE/CMakeLists.txt b/src/DETHRACE/CMakeLists.txt index 47a9ee1a..45c739c4 100644 --- a/src/DETHRACE/CMakeLists.txt +++ b/src/DETHRACE/CMakeLists.txt @@ -1,6 +1,8 @@ # Create object files so we can link them into the main binary and into tests without building twice. add_library(dethrace_obj OBJECT) +option(DETHRACE_REPLAY_DEBUG "Debug replay" OFF) + target_include_directories(dethrace_obj PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} @@ -43,6 +45,10 @@ else() target_compile_definitions(dethrace_obj PRIVATE BR_ENDIAN_LITTLE=1) endif() +if(DETHRACE_REPLAY_DEBUG) + target_compile_definitions(dethrace_obj PRIVATE DETHRACE_REPLAY_DEBUG) +endif() + target_sources(dethrace_obj PRIVATE common/brucetrk.c common/brucetrk.h diff --git a/src/DETHRACE/common/car.c b/src/DETHRACE/common/car.c index ef4c550d..761279f9 100644 --- a/src/DETHRACE/common/car.c +++ b/src/DETHRACE/common/car.c @@ -21,6 +21,7 @@ #include "oil.h" #include "opponent.h" #include "pd/sys.h" +#include "pedestrn.h" #include "piping.h" #include "pratcam.h" #include "raycast.h" @@ -34,8 +35,8 @@ #include #include -int gDoing_physics; -br_scalar gDt; +int gDoing_physics = 0; +br_scalar gDt = 0.f; // suffix added to avoid duplicate symbol int gCollision_detection_on__car = 1; // suffix added to avoid duplicate symbol @@ -51,9 +52,9 @@ void (*ControlCar[6])(tCar_spec*, br_scalar) = { }; int gControl__car = 3; // suffix added to avoid duplicate symbol int gFace_num__car = 1; // suffix added to avoid duplicate symbol -br_angle gOld_yaw__car; // suffix added to avoid duplicate symbol -br_angle gOld_zoom; -br_vector3 gCamera_pos_before_collide; +br_angle gOld_yaw__car = 0; // suffix added to avoid duplicate symbol +br_angle gOld_zoom = 0; +br_vector3 gCamera_pos_before_collide = { { 0 } }; int gMetal_crunch_sound_id__car[5] = { // suffix added to avoid duplicate symbol 5000, @@ -69,24 +70,24 @@ int gMetal_scrape_sound_id__car[3] = { 5012, }; int gCar_car_collisions = 1; -int gFreeze_mechanics; -tU32 gLast_cunning_stunt; -tU32 gWild_start; -tU32 gQuite_wild_start; -tU32 gQuite_wild_end; -tU32 gOn_me_wheels_start; -int gWoz_upside_down_at_all; -tS3_sound_tag gSkid_tag[2]; -tCar_spec* gLast_car_to_skid[2]; -int gEliminate_faces; -br_vector3 gZero_v__car; // suffix added to avoid duplicate symbol -tU32 gSwitch_time; +int gFreeze_mechanics = 0; +tU32 gLast_cunning_stunt = 0; +tU32 gWild_start = 0; +tU32 gQuite_wild_start = 0; +tU32 gQuite_wild_end = 0; +tU32 gOn_me_wheels_start = 0; +int gWoz_upside_down_at_all = 0; +tS3_sound_tag gSkid_tag[2] = { 0, 0 }; +tCar_spec* gLast_car_to_skid[2] = { NULL, NULL}; +int gEliminate_faces = 0; +br_vector3 gZero_v__car = { { 0 } }; // suffix added to avoid duplicate symbol +tU32 gSwitch_time = 0; tSave_camera gSave_camera[2]; tU32 gLast_mechanics_time; int gOpponent_viewing_mode; -int gNet_player_to_view_index; -int gDouble_pling_water; -int gStop_opponents_moving; +int gNet_player_to_view_index = -1; +int gDouble_pling_water = 0; +int gStop_opponents_moving = 0; float gDefensive_powerup_factor[6] = { 1.0f, 0.825f, 0.65f, 0.475f, 0.3f, 0.01f }; float gOffensive_powerup_factor[6] = { 1.0f, 1.5f, 2.0f, 3.0f, 5.0f, 10.0f }; float gEngine_powerup_factor[6] = { 1.3f, 1.9f, 2.5f, 3.2f, 4.0f, 10.0f }; @@ -4505,8 +4506,14 @@ void PutOpponentsInNeutral() { // IDA: void __cdecl SetPanningFieldOfView() void SetPanningFieldOfView() { br_camera* camera_ptr; + static br_angle panning_angle = 0; // Added by DethRace LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + camera_ptr = gCamera->type_data; + if (panning_angle == 0) { + panning_angle = BrDegreeToAngle(gCamera_angle) * 0.7f; + } + camera_ptr->field_of_view = panning_angle; } // IDA: void __usercall CheckDisablePlingMaterials(tCar_spec *pCar@) @@ -4515,7 +4522,23 @@ void CheckDisablePlingMaterials(tCar_spec* pCar) { br_scalar height; int i; LOG_TRACE("(%p)", pCar); - STUB_ONCE(); + + height = 0.f; + if (pCar->water_d == 10000.f) { + DisablePlingMaterials(); + } else { + mat = &pCar->car_master_actor->t.t.mat; + for (i = 0; i < 3; i++) { + if (mat->m[i][1] > 0.f) { + height += pCar->bounds[0].max.v[i] * mat->m[i][1]; + } else { + height += pCar->bounds[0].min.v[i] * mat->m[i][1]; + } + } + if (mat->m[3][1] / WORLD_SCALE + height < pCar->water_d) { + DisablePlingMaterials(); + } + } } // IDA: void __usercall PositionExternalCamera(tCar_spec *c@, tU32 pTime@) @@ -4537,23 +4560,25 @@ void PositionExternalCamera(tCar_spec* c, tU32 pTime) { } else { c = gCar_to_view; } - if (c->car_master_actor->t.t.mat.m[3][0] <= 500.0) { + if (c->car_master_actor->t.t.translate.t.v[0] <= 500.0) { if (gAction_replay_mode && gAction_replay_camera_mode) { - LOG_PANIC("%d, %d", gAction_replay_mode, gAction_replay_camera_mode); - if (gAction_replay_camera_mode == eAction_replay_action && (CheckDisablePlingMaterials(c), IncidentCam(c, pTime))) { - SetPanningFieldOfView(); - EnablePlingMaterials(); - old_camera_mode = gAction_replay_camera_mode; - } else { + if (gAction_replay_camera_mode == eAction_replay_action) { CheckDisablePlingMaterials(c); - SetPanningFieldOfView(); - if (gAction_replay_camera_mode != old_camera_mode) { - SetUpPanningCamera(c); + if (IncidentCam(c, pTime)) { + SetPanningFieldOfView(); + EnablePlingMaterials(); old_camera_mode = gAction_replay_camera_mode; + return; } - PanningExternalCamera(c, pTime); - EnablePlingMaterials(); } + CheckDisablePlingMaterials(c); + SetPanningFieldOfView(); + if (gAction_replay_camera_mode != old_camera_mode) { + SetUpPanningCamera(c); + old_camera_mode = gAction_replay_camera_mode; + } + PanningExternalCamera(c, pTime); + EnablePlingMaterials(); } else { NormalPositionExternalCamera(c, pTime); } @@ -4567,16 +4592,25 @@ void CameraBugFix(tCar_spec* c, tU32 pTime) { br_matrix34* m2; br_vector3 tv; LOG_TRACE("(%p, %d)", c, pTime); - STUB_ONCE(); -} + if (gAction_replay_mode && gAction_replay_camera_mode != eAction_replay_standard && gPed_actor != NULL && !gProgram_state.cockpit_on) { + IncidentCam(c, pTime); + } +} // IDA: int __usercall PossibleRemoveNonCarFromWorld@(br_actor *pActor@) int PossibleRemoveNonCarFromWorld(br_actor* pActor) { tU8 cx; tU8 cz; tTrack_spec* track_spec; LOG_TRACE("(%p)", pActor); - NOT_IMPLEMENTED(); + + track_spec = &gProgram_state.track_spec; + XZToColumnXZ(&cx, &cz, pActor->t.t.translate.t.v[0], pActor->t.t.translate.t.v[2], track_spec); + if (track_spec->columns[cz][cx] == pActor->parent) { + BrActorRemove(pActor); + return 1; + } + return 0; } // IDA: void __usercall PutNonCarBackInWorld(br_actor *pActor@) @@ -4585,7 +4619,10 @@ void PutNonCarBackInWorld(br_actor* pActor) { tU8 cz; tTrack_spec* track_spec; LOG_TRACE("(%p)", pActor); - NOT_IMPLEMENTED(); + + track_spec = &gProgram_state.track_spec; + XZToColumnXZ(&cx, &cz, pActor->t.t.translate.t.v[0], pActor->t.t.translate.t.v[2], track_spec); + BrActorAdd(track_spec->columns[cz][cx], pActor); } // IDA: int __usercall IncidentCam@(tCar_spec *c@, tU32 pTime@) @@ -4599,17 +4636,125 @@ int IncidentCam(tCar_spec* c, tU32 pTime) { br_vector3 murderer_pos; br_scalar ts; tCar_spec* car2; - static tU32 next_incident_time; + static tU32 next_incident_time = 0; static tIncident_type type = eNo_incident; static float severity; static tIncident_info info; - static int random; - static int count; + static int random = 1; + static int count = 0; br_scalar temp; br_vector3 old_cam_pos; int removed; LOG_TRACE("(%p, %d)", c, pTime); - NOT_IMPLEMENTED(); + + gPed_actor = NULL; + m2 = &gCamera->t.t.mat; + if (type == eNo_incident) { + MoveCamToIncident(c, &type, &severity, &info, &next_incident_time); + } + if (type == eNo_incident) { + return 0; + } + if (type == eIncident_ped) { + BrVector3Copy(&old_cam_pos, &gCamera->t.t.translate.t); + gPed_actor = info.ped_info.ped_actor; + removed = PossibleRemoveNonCarFromWorld(info.ped_info.murderer_actor); + BrMatrix34Mul(&mat, &gPed_actor->t.t.mat, &gPed_actor->parent->t.t.mat); + info.ped_info.murderer_actor = c->car_master_actor; + if (info.ped_info.murderer_actor != NULL) { + BrVector3Copy(&murderer_pos, &c->pos); + } else if (info.ped_info.murderer_actor->model != NULL) { + BrVector3Add(&murderer_pos, &info.ped_info.murderer_actor->model->bounds.max, &info.ped_info.murderer_actor->model->bounds.min); + BrVector3Scale(&murderer_pos, &murderer_pos, 0.5f); + BrMatrix34ApplyP(&murderer_pos, &murderer_pos, &info.ped_info.murderer_actor->t.t.mat); + } else { + BrVector3Copy(&murderer_pos, &info.ped_info.murderer_actor->t.t.translate.t); + } + BrVector3Normalise(&vertical, (br_vector3*)mat.m[1]); + BrVector3Scale(&vertical, &vertical, PedHeightFromActor(info.ped_info.ped_actor) / 2.f); + BrVector3Accumulate((br_vector3*)mat.m[3], &vertical); + if (next_incident_time > GetTotalTime() || !PipeSearchForwards()) { + BrVector3Sub(&tv, (br_vector3*)mat.m[3], &murderer_pos); + tv.v[1] = 0.f; + BrVector3Normalise(&tv, &tv); + BrVector3Set(&vertical, .0f, .4f, .0f); + BrVector3Cross(&perp, &tv, &vertical); + if (random) { + BrVector3Negate(&perp, &perp); + } + if (PipeSearchForwards()) { + BrVector3Accumulate(&perp, &tv); + } + BrVector3Add(&gCamera->t.t.translate.t, (br_vector3*)mat.m[3], &perp); + CollideCamera2(&murderer_pos, &gCamera->t.t.translate.t, NULL, 1); + } + PointCamera((br_vector3*)mat.m[3], m2); + BrVector3Sub(&tv, &gCamera->t.t.translate.t, &info.ped_info.murderer_actor->t.t.translate.t); + ts = BrVector3LengthSquared(&tv); + if (abs(GetTotalTime() - next_incident_time) > 2500) { + type = eNo_incident; + } + if ((PipeSearchForwards() ? (next_incident_time < GetTotalTime()) : (next_incident_time > GetTotalTime())) + && (ts > 25.f || CheckForWall(&info.ped_info.murderer_actor->t.t.translate.t, &gCamera->t.t.translate.t))) { + type = eNo_incident; + } + if (removed) { + PutNonCarBackInWorld(info.ped_info.murderer_actor); + } + if (Vector3DistanceSquared((br_vector3*)mat.m[3], &gCamera->t.t.translate.t) < .15f * .15f) { + BrVector3Copy(&gCamera->t.t.translate.t, &old_cam_pos); + gPed_actor = NULL; + return 0; + } + } else if (type == eIncident_car) { + BrVector3Sub(&tv, &info.car_info.car->pos, &c->pos); + tv.v[1] = 0.f; + BrVector3Normalise(&tv, &tv); + BrVector3Scale(&tv, &tv, 2.f); + BrVector3Add(&gCamera->t.t.translate.t, &info.car_info.car->pos, &tv); + gCamera->t.t.translate.t.v[1] += 1.f; + CollideCamera2(&info.car_info.car->pos, &gCamera->t.t.translate.t, NULL, 1); + PointCamera(&info.car_info.car->pos, m2); + BrVector3Sub(&tv, &gCamera->t.t.translate.t, &c->pos); + ts = BrVector3LengthSquared(&tv); + if (abs(GetTotalTime() - next_incident_time) > 2500) { + type = eNo_incident; + } + if ((PipeSearchForwards() ? (next_incident_time < GetTotalTime()) : (next_incident_time > GetTotalTime())) + && (ts > 25.f || CheckForWall(&c->pos, &gCamera->t.t.translate.t))) { + type = eNo_incident; + } + } else if (type == eIncident_wall) { + PointCamera(&c->pos, m2); + BrVector3Sub(&tv, &gCamera->t.t.translate.t, &c->pos); + ts = BrVector3LengthSquared(&tv); + if (abs(GetTotalTime() - next_incident_time) > 2500) { + type = eNo_incident; + } + if ((PipeSearchForwards() ? (next_incident_time < GetTotalTime()) : (next_incident_time > GetTotalTime())) + && (ts > 25.f || CheckForWall(&c->pos, &gCamera->t.t.translate.t))) { + type = eNo_incident; + } + } else { + type = eNo_incident; + } + if (type == eNo_incident) { + if (count > 1) { + SetUpPanningCamera(c); + return 0; + } else { + count++; + if (IncidentCam(c, pTime)) { + count--; + return 1; + } else { + count--; + return 0; + } + } + } else { + return 1; + } } // IDA: int __usercall MoveCamToIncident@(tCar_spec *c@, tIncident_type *type@, float *severity@, tIncident_info *info@, tU32 *next_incident_time) @@ -4628,7 +4773,67 @@ int MoveCamToIncident(tCar_spec* c, tIncident_type* type, float* severity, tInci br_vector3 perp; int test; LOG_TRACE("(%p, %p, %p, %p, %p)", c, type, severity, info, next_incident_time); - NOT_IMPLEMENTED(); + + test = 0; + if (!GetNextIncident(-1, type, severity, info, next_incident_time)) { + *type = eNo_incident; + } else { + if (abs(*next_incident_time) > 2500) { + *type = eNo_incident; + } else { + t = *next_incident_time; + for (test = 0; GetNextIncident(abs(t), &type2, &severity2, &info2, &next_incident_time2) && test <= 10 && abs(next_incident_time2) <= 3500; test++) { + if ((*type != type2 && type2 < *type) || (*type == type2 && *severity <= severity2)) { + *info = info2; + *severity = severity2; + *type = type2; + *next_incident_time = next_incident_time2; + } + t = next_incident_time2; + } + if (abs(*next_incident_time) > 2500) { + *type = eNo_incident; + } else { + if (*type == eIncident_wall) { + if (*severity < 0.1f) { + *type = eNo_incident; + return 0; + } + ScanCarsPositions(c, &c->pos, 100000.f, -1, abs(*next_incident_time), &pos, &t); + if (t == 0) { + *type = eNo_incident; + } else { + BrVector3Sub(&tv, &pos, &c->pos); + if (BrVector3LengthSquared(&tv) > 102.91955471539592f) { + *type = eNo_incident; + } else { + BrVector3Sub(&tv, &pos, &info->wall_info.pos); + BrVector3Normalise(&tv, &tv); + BrVector3Scale(&tv, &tv, 2.f); + BrVector3Set(&vertical, 0.f, 1.f, 0.f); + BrVector3Cross(&perp, &vertical, &tv); + BrVector3Add(&left, &pos, &tv); + BrVector3Add(&left, &left, &perp); + left.v[1] += 2.f; + BrVector3Add(&right, &pos, &tv); + BrVector3Sub(&right, &right, &perp); + right.v[1] += 2.f; + CollideCamera2(&pos, &left, NULL, 1); + CollideCamera2(&pos, &right, NULL, 1); + if (Vector3DistanceSquared(&left, &pos) <= Vector3DistanceSquared(&right, &pos)) { + BrVector3Copy(&tv, &right); + } else { + BrVector3Copy(&tv, &left); + } + BrVector3Copy(&gCamera->t.t.translate.t, &tv); + } + } + } + *next_incident_time += GetTotalTime(); + } + } + } + return 0; } // IDA: void __usercall PanningExternalCamera(tCar_spec *c@, tU32 pTime@) @@ -4637,9 +4842,22 @@ void PanningExternalCamera(tCar_spec* c, tU32 pTime) { br_matrix34* m1; br_vector3 tv; br_scalar ts; - static int inside_camera_zone; + static int inside_camera_zone = 1; LOG_TRACE("(%p, %d)", c, pTime); - NOT_IMPLEMENTED(); + + BrVector3Sub(&tv, &gCamera->t.t.translate.t, &c->pos); + ts = BrVector3LengthSquared(&tv); + if (ts > 102.91955471539592f || (gSwitch_time != 0 && (PipeSearchForwards() ? (gSwitch_time <= GetTotalTime()) : (gSwitch_time >= GetTotalTime())))) { + if ((inside_camera_zone || ts > 205.83910943079184f) && (ts > 25.f || CheckForWall(&c->pos, &gCamera->t.t.translate.t))) { + SetUpPanningCamera(c); + inside_camera_zone = 0; + } + } else { + inside_camera_zone = 1; + } + m1 = &c->car_master_actor->t.t.mat; + m2 = &gCamera->t.t.mat; + PointCameraAtCar(c, m1, m2); } // IDA: int __usercall CheckForWall@(br_vector3 *start@, br_vector3 *end@) @@ -4649,7 +4867,10 @@ int CheckForWall(br_vector3* start, br_vector3* end) { br_vector3 normal; br_scalar d; LOG_TRACE("(%p, %p)", start, end); - NOT_IMPLEMENTED(); + + BrVector3Sub(&dir, end, start); + FindFace(start, &dir, &normal, &d, &material); + return d <= 1.f; } // IDA: void __usercall SetUpPanningCamera(tCar_spec *c@) @@ -4672,7 +4893,64 @@ void SetUpPanningCamera(tCar_spec* c) { int left_score; int right_score; LOG_TRACE("(%p)", c); - NOT_IMPLEMENTED(); + + ScanCarsPositions(c, &c->pos, 411.6782f, -1, 5000, &car_centre, &t); + BrVector3Sub(&dir, &car_centre, &c->pos); + time_step = ((t > GetTotalTime()) ? t - GetTotalTime() : GetTotalTime() - t) * SRandomBetween(0.8f, 1.5f); + if (BrVector3LengthSquared(&dir) >= .01f && t != 0) { + ScanCarsPositions(c, &c->pos, 102.9196f, -1, time_step / 2, &pos, &t2); + if (t2 == 0) { + BrVector3Copy(&pos, &c->pos); + } + } else { + BrVector3Negate(&dir, (br_vector3*)&c->car_master_actor->t.t.mat.m[2]); + BrVector3Copy(&pos, &c->pos); + time_step = 0; + } + BrVector3SetFloat(&tv, 0.f, 1.f, 0.f); + BrVector3Cross(&perp, &tv, &dir); + ts = BrVector3Length(&perp); + if (ts >= .1f) { + BrVector3Scale(&perp, &perp, 2.f / ts * SRandomBetween(0.3333333f, 1.f)); + BrVector3Set(&tv2, 0.f, 2 * SRandomBetween(0.3333333f, 1.f), 0.f); + BrVector3Add(&tv, &pos, &tv2); + BrVector3Add(&left, &tv, &perp); + BrVector3Sub(&right, &tv, &perp); + CollideCamera2(&pos, &left, NULL, 1); + CollideCamera2(&pos, &right, NULL, 1); + BrVector3Sub(&tv, &left, &pos); + BrVector3Sub(&tv2, &right, &pos); + if (BrVector3LengthSquared(&tv) + SRandomPosNeg(.01f) <= BrVector3LengthSquared(&tv2)) { + BrVector3Copy(&gCamera->t.t.translate.t, &right); + } else { + BrVector3Copy(&gCamera->t.t.translate.t, &left); + } + if (t != 0 && CheckForWall(&c->pos, &gCamera->t.t.translate.t)) { + ScanCarsPositions(c, &c->pos, 10000.f, -1, 1000, &tv, &time); + CollideCamera2(&tv, &gCamera->t.t.translate.t, NULL, 1); + } + if (t != 0 && CheckForWall(&car_centre, &gCamera->t.t.translate.t)) { + time_step = time_step / 16; + BrVector3Copy(&tv, &pos); + while (1) { + ScanCarsPositions(c, &tv, 10000.f, abs(t2 - GetTotalTime()), time_step, &tv2, &time); + t2 += (GetReplayDirection() ? 1 : -1) * time_step; + BrVector3Copy(&tv, &tv2); + if (CheckForWall(&tv, &gCamera->t.t.translate.t)) { + break; + } + if (t2 >= GetTotalTime() + 5000) { + break; + } + } + gSwitch_time = t2; + } else { + if (t == 0) { + t = 5000; + } + gSwitch_time = t; + } + } } // IDA: void __usercall SaveCameraPosition(int i@) @@ -4729,7 +5007,7 @@ void NormalPositionExternalCamera(tCar_spec* c, tU32 pTime) { swoop = gCountdown && c->pos.v[1] + 0.001f < gCamera_height; manual_swing = gOld_yaw__car != gCamera_yaw || swoop; manual_zoom = (double)gOld_zoom != gCamera_zoom; - old_camera_pos = *(br_vector3*)&m1->m[3][0]; + BrVector3Copy(&old_camera_pos, &gCamera->t.t.translate.t); if (!gProgram_state.cockpit_on) { if (swoop) { gCamera_yaw = 0; @@ -4737,10 +5015,7 @@ void NormalPositionExternalCamera(tCar_spec* c, tU32 pTime) { } if (fabs(c->speedo_speed) > 0.0006f && gCamera_mode > 0) { gCamera_mode = -1; - gCamera_sign = m2->m[2][1] * c->direction.v[1] - + m2->m[2][2] * c->direction.v[2] - + m2->m[2][0] * c->direction.v[0] - > 0.0; + gCamera_sign = BrVector3Dot((br_vector3*)m2->m[2], &c->direction) > 0.0f; } if (c->frame_collision_flag && gCamera_mode != -2) { gCamera_mode = 1; @@ -4748,34 +5023,32 @@ void NormalPositionExternalCamera(tCar_spec* c, tU32 pTime) { if (gCar_flying || gCamera_reset || gCamera_mode == -2) { gCamera_mode = 0; } - d = sqrtf((float)gCamera_zoom) + 0.57971013f; + d = sqrtf(gCamera_zoom) + 0.57971013f; if (!gCamera_mode || gCamera_mode == -1) { - vn = c->direction; + BrVector3Copy(&vn, &c->direction); MoveWithWheels(c, &vn, manual_swing); - vn.v[1] = 0.0; + vn.v[1] = 0.0f; BrVector3Normalise(&vn, &vn); if (gCar_flying) { gCamera_sign = 0; } SwingCamera(c, m2, m1, &vn, pTime); BrVector3Scale(&a, &vn, d); - m1->m[3][0] = c->pos.v[0] - a.v[0]; - m1->m[3][1] = c->pos.v[1] - a.v[1]; - m1->m[3][2] = c->pos.v[2] - a.v[2]; - gView_direction = vn; + BrVector3Sub(&gCamera->t.t.translate.t, &c->pos, &a); + BrVector3Copy(&gView_direction, &vn); } if (gCamera_mode == 1) { if (manual_swing || manual_zoom) { - old_camera_pos = gCamera_pos_before_collide; + BrVector3Copy(&old_camera_pos, &gCamera_pos_before_collide); } BrVector3Sub(&a, &c->pos, &old_camera_pos); - a.v[1] = 0.0; + a.v[1] = 0.0f; if (manual_swing) { DrVector3RotateY(&a, (gCamera_sign == 0 ? 1 : -1) * (gCamera_yaw - gOld_yaw__car)); gCamera_yaw = gOld_yaw__car; } BrVector3Normalise(&vn, &a); - gView_direction = vn; + BrVector3Copy(&gView_direction, &vn); BrVector3Scale(&vn, &vn, -d); BrVector3Accumulate(&a, &vn); dist = BrVector3Length(&a); @@ -4784,28 +5057,24 @@ void NormalPositionExternalCamera(tCar_spec* c, tU32 pTime) { BrVector3Scale(&a, &a, (l - 1.f)); BrVector3Accumulate(&vn, &a); } - m1->m[3][0] = c->pos.v[0] + vn.v[0]; - m1->m[3][1] = c->pos.v[1] + vn.v[1]; - m1->m[3][2] = c->pos.v[2] + vn.v[2]; + BrVector3Add(&gCamera->t.t.translate.t, &c->pos, &vn); } height_inc = gCamera_zoom * gCamera_zoom + 0.3f; time = pTime * 0.001f; if (!gCamera_frozen || gAction_replay_mode) { - if (pTime < 5000) { - if (swoop) { - if (time > 0.2) { - time = 0.2; - } - gCamera_height -= time * 5.0; - if (gCamera_height < c->pos.v[1]) { - gCamera_height = c->pos.v[1]; - } - } else { - gCamera_height = time * 5.0 * c->pos.v[1] + gCamera_height; - gCamera_height = gCamera_height / (time * 5.0 + 1.0); + if (pTime >= 5000) { + gCamera_height = c->pos.v[1]; + } else if (swoop) { + if (time > 0.2f) { + time = 0.2f; + } + gCamera_height -= time * 5.0f; + if (gCamera_height < c->pos.v[1]) { + gCamera_height = c->pos.v[1]; } } else { - gCamera_height = c->pos.v[1]; + gCamera_height = time * 5.0f * c->pos.v[1] + gCamera_height; + gCamera_height = gCamera_height / (time * 5.0f + 1.0f); } } l = c->direction.v[1] * d; @@ -4815,14 +5084,10 @@ void NormalPositionExternalCamera(tCar_spec* c, tU32 pTime) { } } - m1->m[3][1] = height_inc + gCamera_height; - gCamera_pos_before_collide = *(br_vector3*)&m1->m[3][0]; - CollideCameraWithOtherCars(&c->pos, (br_vector3*)m1->m[3]); - if (manual_swing || manual_zoom) { - CollideCamera2(&c->pos, (br_vector3*)m1->m[3], &old_camera_pos, 1); - } else { - CollideCamera2(&c->pos, (br_vector3*)m1->m[3], &old_camera_pos, 0); - } + gCamera->t.t.translate.t.v[1] = height_inc + gCamera_height; + BrVector3Copy(&gCamera_pos_before_collide, &gCamera->t.t.translate.t); + CollideCameraWithOtherCars(&c->pos, &gCamera->t.t.translate.t); + CollideCamera2(&c->pos, &gCamera->t.t.translate.t, &old_camera_pos, manual_swing || manual_zoom); if (gCamera_has_collided && swoop) { gCamera_height = c->pos.v[1]; } @@ -5045,7 +5310,23 @@ void PointCamera(br_vector3* pos, br_matrix34* m2) { br_angle theta; br_camera* camera_ptr; LOG_TRACE("(%p, %p)", pos, m2); - NOT_IMPLEMENTED(); + + camera_ptr = gCamera->type_data; + BrVector3Sub(&vn, pos, (br_vector3*)m2->m[3]); + vn.v[1] = 0.f; + BrVector3Normalise(&vn, &vn); + m2->m[0][0] = -vn.v[2]; + m2->m[0][1] = 0.f; + m2->m[0][2] = vn.v[0]; + m2->m[1][0] = 0.f; + m2->m[1][1] = 1.f; + m2->m[1][2] = 0.f; + m2->m[2][0] = -vn.v[0]; + m2->m[2][1] = 0.f; + m2->m[2][2] = -vn.v[2]; + dist = BrVector3LengthSquared(&vn); + theta = BR_ATAN2(m2->m[3][1] - pos->v[1], dist); + BrMatrix34PreRotateX(m2, camera_ptr->field_of_view / 5 - theta); } // IDA: int __usercall CollideCamera2@(br_vector3 *car_pos@, br_vector3 *cam_pos@, br_vector3 *old_camera_pos@, int manual_move@) @@ -6963,14 +7244,31 @@ void AdjustNonCar(br_actor* pActor, br_matrix34* pMat) { tU8 cz; tTrack_spec* track_spec; LOG_TRACE("(%p, %p)", pActor, pMat); - NOT_IMPLEMENTED(); + + track_spec = &gProgram_state.track_spec; + BrMatrix34Copy(&pActor->t.t.mat, pMat); + if (pActor->parent != gNon_track_actor) { + XZToColumnXZ(&cx, &cz, pActor->t.t.translate.t.v[0], pActor->t.t.translate.t.v[2], track_spec); + if (track_spec->columns[cz][cx] != pActor->parent && track_spec->columns[cz][cx] != NULL) { + BrActorRemove(pActor); + BrActorAdd(track_spec->columns[cz][cx], pActor); + } + } } // IDA: void __usercall PipeSingleNonCar(tCollision_info *c@) void PipeSingleNonCar(tCollision_info* c) { LOG_TRACE("(%p)", c); - STUB_ONCE(); + StartPipingSession(ePipe_chunk_non_car); + if (gDoing_physics) { + BrVector3InvScale(&c->car_master_actor->t.t.translate.t, &c->car_master_actor->t.t.translate.t, WORLD_SCALE); + } + AddNonCarToPipingSession(c->car_ID, c->car_master_actor); + if (gDoing_physics) { + BrVector3Scale(&c->car_master_actor->t.t.translate.t, &c->car_master_actor->t.t.translate.t, WORLD_SCALE); + } + EndPipingSession(); } // IDA: int __usercall GetPrecalculatedFacesUnderCar@(tCar_spec *pCar@, tFace_ref **pFace_refs@) diff --git a/src/DETHRACE/common/graphics.c b/src/DETHRACE/common/graphics.c index 57abe39e..9decd133 100644 --- a/src/DETHRACE/common/graphics.c +++ b/src/DETHRACE/common/graphics.c @@ -770,13 +770,16 @@ void NewScreenWobble(double pAmplitude_x, double pAmplitude_y, double pPeriod) { // IDA: void __usercall SetScreenWobble(int pWobble_x@, int pWobble_y@) void SetScreenWobble(int pWobble_x, int pWobble_y) { LOG_TRACE("(%d, %d)", pWobble_x, pWobble_y); - NOT_IMPLEMENTED(); + + gScreen_wobble_y = pWobble_y; + gScreen_wobble_x = pWobble_x; } // IDA: void __cdecl ResetScreenWobble() void ResetScreenWobble() { LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + SetScreenWobble(0, 0); } // IDA: void __usercall CalculateWobblitude(tU32 pThe_time@) @@ -1784,7 +1787,7 @@ void RenderAFrame(int pDepth_mask_on) { PipeFrameFinish(); } gRender_screen->pixels = old_pixels; - if (!gPalette_fade_time || GetRaceTime() > (gPalette_fade_time + 500)) { + if (!gPalette_fade_time || GetRaceTime() > gPalette_fade_time + 500) { PDScreenBufferSwap(0); } if (gAction_replay_mode) { @@ -2092,7 +2095,15 @@ void DRPixelmapRectangleMaskedCopy(br_pixelmap* pDest, br_int_16 pDest_x, br_int // IDA: void __usercall DRMaskedStamp(br_int_16 pDest_x@, br_int_16 pDest_y@, br_pixelmap *pSource@) void DRMaskedStamp(br_int_16 pDest_x, br_int_16 pDest_y, br_pixelmap* pSource) { LOG_TRACE("(%d, %d, %p)", pDest_x, pDest_y, pSource); - NOT_IMPLEMENTED(); + + DRPixelmapRectangleMaskedCopy(gBack_screen, + pDest_x, + pDest_y, + pSource, + 0, + 0, + pSource->width, + pSource->height); } // IDA: void __usercall DRPixelmapRectangleOnscreenCopy(br_pixelmap *pDest@, br_int_16 pDest_x@, br_int_16 pDest_y@, br_pixelmap *pSource@, br_int_16 pSource_x, br_int_16 pSource_y, br_int_16 pWidth, br_int_16 pHeight) diff --git a/src/DETHRACE/common/pedestrn.c b/src/DETHRACE/common/pedestrn.c index 2c529a3a..8b4cf631 100644 --- a/src/DETHRACE/common/pedestrn.c +++ b/src/DETHRACE/common/pedestrn.c @@ -2295,7 +2295,7 @@ void RevivePedestrian(tPedestrian_data* pPedestrian, int pAnimate) { pPedestrian->fate = NULL; gInitial_instruction = NULL; PedestrianNextInstruction(pPedestrian, 0.f, 1, 0); - pPedestrian->from_pos = pPedestrian->actor->t.t.translate.t; + BrVector3Copy(&pPedestrian->from_pos, &pPedestrian->actor->t.t.translate.t); MungePedModel(pPedestrian); pPedestrian->pos.v[V_Y] += pPedestrian->sequences[pPedestrian->current_sequence].frames[0].offset.v[V_Y]; } diff --git a/src/DETHRACE/common/piping.c b/src/DETHRACE/common/piping.c index 5ff77232..2d5db318 100644 --- a/src/DETHRACE/common/piping.c +++ b/src/DETHRACE/common/piping.c @@ -1,19 +1,64 @@ #include "piping.h" #include "brender/brender.h" +#include "car.h" +#include "crush.h" +#include "errors.h" #include "globvars.h" +#include "globvrpb.h" +#include "graphics.h" #include "harness/trace.h" +#include "oil.h" #include "opponent.h" #include "pedestrn.h" +#include "replay.h" +#include "skidmark.h" +#include "spark.h" +#include "sys.h" #include "sound.h" +#include "utility.h" +#include "world.h" #include #include -tU8* gPipe_buffer_start; -int gDisable_sound; -int gDisable_advance; -int gMax_rewind_chunks; -float gWall_severity; -tPipe_reset_proc* gReset_procs[32]; +tU8* gPipe_buffer_start = NULL; +int gDisable_sound = 0; +int gDisable_advance = 0; +int gMax_rewind_chunks = 1000; +float gWall_severity = 0.f; +tPipe_reset_proc* gReset_procs[32] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + ResetAllPedestrians, + NULL, + NULL, + NULL, + NULL, + NULL, + ResetAllPedGibs, + NULL, + ResetSparks, + ResetShrapnel, + ResetScreenWobble, + NULL, + NULL, + ResetSmoke, + NULL, + NULL, + NULL, + NULL, + NULL, + ResetProxRay, + NULL, +}; tPiped_registration_snapshot gRegistration_snapshots[5]; tPipe_smudge_data* gSmudge_space; tU32 gOldest_time; @@ -43,6 +88,21 @@ tU8* gPipe_buffer_oldest; tU32 gPipe_buffer_size; tU8* gLocal_buffer; tU32 gLocal_buffer_size; +tPipe_chunk *gIncidentChunk; // FIXME: added by DethRace (really needed?) + +#define LOCAL_BUFFER_SIZE 15000 + +#if DETHRACE_REPLAY_DEBUG +#define REPLAY_DEBUG_MAGIC1 0x1ed6ef85 +#define REPLAY_DEBUG_ASSERT(test) assert(test) +#include +#else +#define REPLAY_DEBUG_ASSERT(test) +#endif + +#if defined(DETHRACE_FIX_BUGS) +#define PIPE_ALIGN(V) (((V) + sizeof(void*) - 1) & ~(sizeof(void*) - 1)) +#endif // IDA: void __usercall GetReducedPos(br_vector3 *v@, tReduced_pos *p@) void GetReducedPos(br_vector3* v, tReduced_pos* p) { @@ -50,7 +110,7 @@ void GetReducedPos(br_vector3* v, tReduced_pos* p) { v->v[0] = p->v[0] / 800.f; v->v[1] = p->v[1] / 800.f; - v->v[2] = p->v[1] / 800.f; + v->v[2] = p->v[2] / 800.f; BrVector3Accumulate(v, &gProgram_state.current_car.car_master_actor->t.t.translate.t); } @@ -68,7 +128,18 @@ void SaveReducedPos(tReduced_pos* p, br_vector3* v) { // IDA: int __cdecl PipeSearchForwards() int PipeSearchForwards() { LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + if (gPipe_play_ptr == gPipe_record_ptr) { + return 0; + } + if (gPipe_play_ptr == gPipe_buffer_oldest) { + return 1; + } + if (GetReplayRate() == 0.f) { + return GetReplayDirection() > 0; + } else { + return GetReplayRate() > 0.f; + } } // IDA: int __cdecl IsActionReplayAvailable() @@ -81,7 +152,9 @@ int IsActionReplayAvailable() { // IDA: int __cdecl SomeReplayLeft() int SomeReplayLeft() { LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + return ((GetReplayDirection() >= 1 && gPipe_play_ptr != gPipe_record_ptr) || + (GetReplayDirection() <= -1 && gPipe_play_ptr != gPipe_buffer_oldest)); } // IDA: void __cdecl DisablePipedSounds() @@ -104,14 +177,162 @@ tU32 LengthOfSession(tPipe_session* pSession) { tU32 running_total; tPipe_chunk* the_chunk; LOG_TRACE("(%p)", pSession); - NOT_IMPLEMENTED(); + +#define SIZEOF_CHUNK(MEMBER) (offsetof(tPipe_chunk, chunk_data) + sizeof(pSession->chunks.chunk_data.MEMBER)) +#define ROUND_UP(V, M) (((V) + (M) - 1) & (~((M) - 1))) + + switch (pSession->chunk_type) { + case ePipe_chunk_actor_rstyle: + running_total = SIZEOF_CHUNK(actor_rstyle_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_actor_translate: + running_total = SIZEOF_CHUNK(actor_translate_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_actor_transform: + running_total = SIZEOF_CHUNK(actor_transform_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_actor_create: + running_total = 0; + break; + case ePipe_chunk_actor_destroy: + running_total = 0; + break; + case ePipe_chunk_actor_relink: + running_total = SIZEOF_CHUNK(actor_relink_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_actor_material: + running_total = SIZEOF_CHUNK(actor_material_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_face_material: + running_total = SIZEOF_CHUNK(face_material_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_material_trans: + running_total = SIZEOF_CHUNK(material_trans_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_material_pixelmap: + running_total = SIZEOF_CHUNK(material_pixelmap_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_model_geometry: + running_total = 0; + for (i = 0; i < pSession->number_of_chunks; i++) { + the_chunk = (tPipe_chunk*)&((tU8*)&pSession->chunks)[running_total]; + running_total += the_chunk->chunk_data.model_geometry_data.vertex_count * sizeof(tChanged_vertex) + offsetof(tPipe_model_geometry_data, vertex_changes) + offsetof(tPipe_chunk, chunk_data); + } + break; + case ePipe_chunk_pedestrian: + running_total = 0; + for (i = 0; i < pSession->number_of_chunks; i++) { + the_chunk = (tPipe_chunk*)&(((tU8*)&pSession->chunks)[running_total]); + if (the_chunk->chunk_data.pedestrian_data.hit_points <= 0) { + running_total += SIZEOF_CHUNK(pedestrian_data); + } else { + running_total += offsetof(tPipe_pedestrian_data, spin_period) + offsetof(tPipe_chunk, chunk_data); + } + } + break; + case ePipe_chunk_frame_boundary: + running_total = SIZEOF_CHUNK(frame_boundary_data); + break; + case ePipe_chunk_car: + running_total = SIZEOF_CHUNK(car_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_sound: + running_total = SIZEOF_CHUNK(sound_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_damage: + running_total = SIZEOF_CHUNK(damage_data); + break; + case ePipe_chunk_special: + running_total = SIZEOF_CHUNK(special_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_ped_gib: + running_total = SIZEOF_CHUNK(ped_gib_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_incident: + running_total = SIZEOF_CHUNK(incident_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_spark: + running_total = SIZEOF_CHUNK(spark_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_shrapnel: + running_total = 0; + for (i = 0; i < pSession->number_of_chunks; i++) { + the_chunk = (tPipe_chunk*)&((tU8*)&pSession->chunks)[running_total]; + if (the_chunk->subject_index & 0x8000) { + running_total += SIZEOF_CHUNK(shrapnel_data); + } else { + running_total += offsetof(tPipe_shrapnel_data, age) + offsetof(tPipe_chunk, chunk_data); + } + } + break; + case ePipe_chunk_screen_shake: + running_total = SIZEOF_CHUNK(screen_shake_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_groove_stop: + running_total = SIZEOF_CHUNK(groove_stop_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_non_car: + running_total = SIZEOF_CHUNK(non_car_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_smoke: + running_total = SIZEOF_CHUNK(smoke_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_oil_spill: + running_total = SIZEOF_CHUNK(oil_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_smoke_column: + running_total = ROUND_UP(SIZEOF_CHUNK(smoke_column_data), 4) * pSession->number_of_chunks; + break; + case ePipe_chunk_flame: + running_total = SIZEOF_CHUNK(flame_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_smudge: + running_total = 0; + for (i = 0; i < pSession->number_of_chunks; i++) { + the_chunk = (tPipe_chunk*)&((tU8*)&pSession->chunks)[running_total]; + running_total += the_chunk->chunk_data.smudge_data.vertex_count * sizeof(tSmudged_vertex) + offsetof(tPipe_smudge_data, vertex_changes) + offsetof(tPipe_chunk, chunk_data); + } + break; + case ePipe_chunk_splash: + running_total = SIZEOF_CHUNK(splash_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_prox_ray: + running_total = SIZEOF_CHUNK(prox_ray_data) * pSession->number_of_chunks; + break; + case ePipe_chunk_skid_adjustment: + running_total = SIZEOF_CHUNK(skid_adjustment) * pSession->number_of_chunks; + break; + default: + running_total = 0; + break; + } + running_total += offsetof(tPipe_session, chunks) + sizeof(tU16); + if (running_total % 2 != 0) { + FatalError(98); + } + return running_total; } // IDA: void __usercall StartPipingSession2(tPipe_chunk_type pThe_type@, int pMunge_reentrancy@) void StartPipingSession2(tPipe_chunk_type pThe_type, int pMunge_reentrancy) { LOG_TRACE("(%d, %d)", pThe_type, pMunge_reentrancy); - STUB_ONCE(); + if (gPipe_buffer_start != NULL && !gAction_replay_mode && gProgram_state.racing) { + if (pMunge_reentrancy) { + if (gReentrancy_count != 0) { + gReentrancy_array[gReentrancy_count - 1] = ((tPipe_session*)gLocal_buffer)->chunk_type; + EndPipingSession2(0); + } + gReentrancy_count++; + } + ((tPipe_session*)gLocal_buffer)->chunk_type = pThe_type; + ((tPipe_session*)gLocal_buffer)->number_of_chunks = 0; +#if defined(DETHRACE_REPLAY_DEBUG) + ((tPipe_session*)gLocal_buffer)->magic1 = REPLAY_DEBUG_MAGIC1; +#endif + gLocal_buffer_size = offsetof(tPipe_session, chunks); + gMr_chunky = &((tPipe_session*)gLocal_buffer)->chunks; + } } // IDA: void __usercall StartPipingSession(tPipe_chunk_type pThe_type@) @@ -126,7 +347,53 @@ void EndPipingSession2(int pMunge_reentrancy) { int a; LOG_TRACE("(%d)", pMunge_reentrancy); - STUB_ONCE(); + if (gPipe_buffer_start != NULL && !gAction_replay_mode && gProgram_state.racing) { + // Each session ends with a tU16, containing the session size + *(tU16*)&gLocal_buffer[gLocal_buffer_size] = gLocal_buffer_size; + a = gLocal_buffer_size; + gLocal_buffer_size += sizeof(tU16); + REPLAY_DEBUG_ASSERT(LengthOfSession((tPipe_session*)gLocal_buffer) == gLocal_buffer_size); +#if defined(DETHRACE_FIX_BUGS) + gLocal_buffer_size = PIPE_ALIGN(gLocal_buffer_size); + *(tU16*)&gLocal_buffer[gLocal_buffer_size - sizeof(tU16)] = gLocal_buffer_size - sizeof(tU16); +#endif + if (((tPipe_session*)gLocal_buffer)->number_of_chunks != 0 && (gLocal_buffer_size < LOCAL_BUFFER_SIZE || a == LOCAL_BUFFER_SIZE - 2)) { + if (gPipe_buffer_phys_end < gPipe_record_ptr + gLocal_buffer_size) { + // Put session at begin of pipe, as no place at end + gPipe_buffer_working_end = gPipe_record_ptr; + gPipe_buffer_oldest = gPipe_buffer_start; + gPipe_record_ptr = gPipe_buffer_start; + } + while (gPipe_record_ptr <= gPipe_buffer_oldest && gPipe_buffer_oldest < gPipe_record_ptr + gLocal_buffer_size) { + // Remove older sessions +#if defined(DETHRACE_FIX_BUGS) + gPipe_buffer_oldest += PIPE_ALIGN(LengthOfSession((tPipe_session*)gPipe_buffer_oldest)); +#else + gPipe_buffer_oldest += LengthOfSession((tPipe_session*)gPipe_buffer_oldest); +#endif + if (gPipe_buffer_working_end <= gPipe_buffer_oldest) { + gPipe_buffer_working_end = gPipe_buffer_phys_end; + gPipe_buffer_oldest = gPipe_buffer_start; + } + } + if (gPipe_buffer_oldest == NULL) { + gPipe_buffer_oldest = gPipe_record_ptr; + } + memcpy(gPipe_record_ptr, gLocal_buffer, gLocal_buffer_size); + gPipe_record_ptr += gLocal_buffer_size; + if (gPipe_buffer_working_end < gPipe_record_ptr) { + gPipe_buffer_working_end = gPipe_record_ptr; + } + } + if (pMunge_reentrancy) { + if (gReentrancy_count != 0) { + gReentrancy_count--; + if (gReentrancy_count != 0) { + StartPipingSession2(gReentrancy_array[gReentrancy_count - 1], 0); + } + } + } + } } // IDA: void __cdecl EndPipingSession() @@ -142,7 +409,17 @@ void AddDataToSession(int pSubject_index, void* pData, tU32 pData_length) { int variable_for_breaking_on; LOG_TRACE("(%d, %p, %d)", pSubject_index, pData, pData_length); - STUB_ONCE(); + if (gPipe_buffer_start != NULL && !gAction_replay_mode && gProgram_state.racing) { + temp_buffer_size = gLocal_buffer_size + offsetof(tPipe_chunk, chunk_data) + pData_length; + if (temp_buffer_size >= LOCAL_BUFFER_SIZE) { + return; + } + ((tPipe_session*)gLocal_buffer)->number_of_chunks++; + gMr_chunky->subject_index = pSubject_index; + memcpy(&gMr_chunky->chunk_data, pData, pData_length); + gMr_chunky = (tPipe_chunk*)(((tU8*)&gMr_chunky->chunk_data) + pData_length); + gLocal_buffer_size = temp_buffer_size; + } } // IDA: void __usercall AddModelGeometryToPipingSession(tU16 pCar_ID@, int pModel_index@, int pVertex_count@, tChanged_vertex *pCoordinates@) @@ -188,7 +465,7 @@ void AddPedestrianToPipingSession(int pPedestrian_index, br_matrix34* pTrans, tU if (pFrame_index == 0xff) { pFrame_index = 0; } - data.action_and_frame_index = (pAction_index * 16 + pFrame_index) | (pDone_initial ? 0 : 0x80); + data.action_and_frame_index = (pDone_initial ? 1 : 0) << 7 | pAction_index << 4 | pFrame_index; data.hit_points = pHit_points; data.new_translation.v[0] = pTrans->m[3][0]; data.new_translation.v[1] = pTrans->m[3][1]; @@ -285,7 +562,7 @@ void AddSmokeColumnToPipingSession(int pIndex, tCar_spec* pCar, int pVertex, int data.car_ID = pCar->car_ID; data.vertex = pVertex; - AddDataToSession(pIndex + pColour * 0x4000, &data, sizeof(tPipe_smoke_column_data)); + AddDataToSession(pColour << 14 | pIndex, &data, sizeof(tPipe_smoke_column_data)); } // IDA: void __usercall AddFlameToPipingSession(int pIndex@, int pFrame_count@, br_scalar pScale_x, br_scalar pScale_y, br_scalar pOffset_x, br_scalar pOffset_z) @@ -306,7 +583,7 @@ void AddSplashToPipingSession(tCollision_info* pCar) { tPipe_splash_data data; LOG_TRACE("(%p)", pCar); - if (pCar->driver >= 0) { + if (pCar->driver >= eDriver_oppo) { data.d = pCar->water_d; BrVector3Copy(&data.normal, &pCar->water_normal); AddDataToSession(pCar->car_ID, &data, sizeof(tPipe_splash_data)); @@ -351,7 +628,7 @@ void AddCarToPipingSession(int pCar_ID, br_matrix34* pCar_mat, br_vector3* pCar_ data.lr_sus_position = pLr_sus_position * 127.f / .15f; data.rr_sus_position = pRr_sus_position * 127.f / .15f; data.steering_angle = pSteering_angle * 32767.f / 60.f; - data.revs_and_gear = (((int)pRevs) / 10) + (pFrame_coll_flag ? 0 : 0x800) + (pGear + 1) * 0x1000; + data.revs_and_gear = (pGear + 1) << 12 | (pFrame_coll_flag ? 0 : 1) << 11 | ((((int)pRevs) / 10) & 0x7ff); AddDataToSession(pCar_ID, &data, sizeof(tPipe_car_data)); } @@ -491,7 +768,7 @@ void PipeSingleCar(int pCar_ID, br_matrix34* pCar_mat, br_vector3* pCar_velocity void PipeSingleSound(tS3_outlet_ptr pOutlet, int pSound_index, tS3_volume pL_volume, tS3_volume pR_volume, tS3_pitch pPitch, br_vector3* pPos) { LOG_TRACE("(%d, %d, %d, %d, %d, %p)", pOutlet, pSound_index, pL_volume, pR_volume, pPitch, pPos); - if (gProgram_state.racing && !gAction_replay_mode) { + if (!gAction_replay_mode && gProgram_state.racing) { StartPipingSession(ePipe_chunk_sound); AddSoundToPipingSession(pOutlet, pSound_index, pL_volume, pR_volume, pPitch, pPos); EndPipingSession(); @@ -557,12 +834,17 @@ void PipeSinglePedIncident(int pPed_index, br_actor* pActor) { // IDA: void __cdecl PipeSingleWallIncident(float pSeverity, br_vector3 *pImpact_point) void PipeSingleWallIncident(float pSeverity, br_vector3* pImpact_point) { LOG_TRACE("(%f, %p)", pSeverity, pImpact_point); - STUB_ONCE(); + + if (pSeverity > gWall_severity) { + gWall_severity = pSeverity; + BrVector3Copy(&gWall_impact_point, pImpact_point); + } } // IDA: void __usercall PipeSingleScreenShake(int pWobble_x@, int pWobble_y@) void PipeSingleScreenShake(int pWobble_x, int pWobble_y) { LOG_TRACE("(%d, %d)", pWobble_x, pWobble_y); + StartPipingSession(ePipe_chunk_screen_shake); AddScreenWobbleToPipingSession(pWobble_x, pWobble_y); EndPipingSession(); @@ -581,7 +863,16 @@ void PipeSingleGrooveStop(int pGroove_index, br_matrix34* pMatrix, int pPath_int // IDA: void __cdecl PipeFrameFinish() void PipeFrameFinish() { LOG_TRACE("()"); - STUB_ONCE(); + + if (gWall_severity != 0.f) { + StartPipingSession(ePipe_chunk_incident); + AddWallIncidentToPipingSession(gWall_severity, &gWall_impact_point); + EndPipingSession(); + gWall_severity = 0.f; + } + StartPipingSession(ePipe_chunk_frame_boundary); + AddFrameFinishToPipingSession(GetTotalTime()); + EndPipingSession(); } // IDA: void __cdecl PipingFrameReset() @@ -608,20 +899,51 @@ void PipeSingleSkidAdjustment(int pSkid_num, br_matrix34* pMatrix, int pMaterial // IDA: void __cdecl ResetPiping() void ResetPiping() { LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + gWall_severity = 0.f; + gPipe_buffer_oldest = NULL; + gPipe_record_ptr = gPipe_buffer_start; + gPipe_buffer_working_end = gPipe_buffer_phys_end; + gReentrancy_count = 0; } // IDA: void __cdecl InitialisePiping() void InitialisePiping() { LOG_TRACE("()"); - STUB_ONCE(); + + if (!gAusterity_mode && gNet_mode == eNet_mode_none) { + PDAllocateActionReplayBuffer((char**)&gPipe_buffer_start, &gPipe_buffer_size); + gPipe_buffer_phys_end = gPipe_buffer_start + gPipe_buffer_size; + gSmudge_space = BrMemAllocate(offsetof(tPipe_smudge_data, vertex_changes) + sizeof(tSmudged_vertex) * 2400, kMem_pipe_model_geometry); + // DAT_00532008 = 0; + BrVector3SetFloat(&gZero_vector, 0.f, 0.f, 0.f); + gModel_geometry_space = (tPipe_model_geometry_data*)gSmudge_space; + gLocal_buffer = BrMemAllocate(LOCAL_BUFFER_SIZE, kMem_pipe_model_geometry); + } else { + gPipe_buffer_start = NULL; + gLocal_buffer = NULL; + gModel_geometry_space = NULL; + gSmudge_space = NULL; + } + ResetPiping(); } // IDA: void __cdecl DisposePiping() void DisposePiping() { LOG_TRACE("()"); - STUB(); + if (gPipe_buffer_start != NULL) { + PDDisposeActionReplayBuffer((char*)gPipe_buffer_start); + } + gPipe_buffer_start = NULL; + if (gModel_geometry_space != NULL) { + BrMemFree(gModel_geometry_space); + gModel_geometry_space = NULL; + } + if (gLocal_buffer != NULL) { + BrMemFree(gLocal_buffer); + gLocal_buffer = NULL; + } } // IDA: void __cdecl InitLastDamageArrayEtc() @@ -650,7 +972,7 @@ void InitLastDamageArrayEtc() { car->frame_start_damage[j] = 0; } } - car->car_ID = (cat << 8) + i; + car->car_ID = (cat << 8) | i; } } } @@ -705,7 +1027,7 @@ void PipeCarPositions() { } else { car = GetCarSpec(cat, i); } - AddCarToPipingSession(cat * 0x100 + i, + AddCarToPipingSession((cat << 8) | i, &car->car_master_actor->t.t.mat, &car->v, car->speedo_speed, car->lf_sus_position, car->rf_sus_position, car->lr_sus_position, car->rr_sus_position, car->steering_angle, car->revs, car->gear, car->frame_collision_flag); @@ -737,7 +1059,7 @@ void PipeCarPositions() { StartPipingSession(ePipe_chunk_damage); session_started = 1; } - AddDamageToPipingSession(cat * 0x100 + i, damage_deltas); + AddDamageToPipingSession((cat << 8) | i, damage_deltas); } } } @@ -750,13 +1072,15 @@ void PipeCarPositions() { // IDA: void __cdecl ResetPipePlayToEnd() void ResetPipePlayToEnd() { LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + gPipe_play_ptr = gPipe_record_ptr; } // IDA: void __cdecl ResetPipePlayToStart() void ResetPipePlayToStart() { LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + gPipe_play_ptr = gPipe_buffer_oldest; } // IDA: tU8* __cdecl GetPipePlayPtr() @@ -777,7 +1101,85 @@ void SetPipePlayPtr(tU8* pPtr) { void AdvanceChunkPtr(tPipe_chunk** pChunk, tChunk_subject_index pType) { tPipe_chunk* old_chunk; LOG_TRACE("(%p, %d)", pChunk, pType); - NOT_IMPLEMENTED(); + + old_chunk = *pChunk; + if (gDisable_advance) { + return; + } + switch (pType) { + case ePipe_chunk_model_geometry: + *(tU8**)pChunk += offsetof(tPipe_model_geometry_data, vertex_changes) + (*pChunk)->chunk_data.model_geometry_data.vertex_count * sizeof(tChanged_vertex); + break; + case ePipe_chunk_pedestrian: + *(tU8**)pChunk += (((*pChunk)->chunk_data.pedestrian_data.hit_points <= 0) ? sizeof(tPipe_pedestrian_data) : offsetof(tPipe_pedestrian_data, spin_period)); + break; + case ePipe_chunk_frame_boundary: + *(tU8**)pChunk += sizeof(tPipe_frame_boundary_data); + break; + case ePipe_chunk_car: + *(tU8**)pChunk += sizeof(tPipe_car_data); + break; + case ePipe_chunk_sound: + *(tU8**)pChunk += sizeof(tPipe_sound_data); + break; + case ePipe_chunk_damage: + *(tU8**)pChunk += sizeof(tPipe_damage_data); + break; + case ePipe_chunk_special: + *(tU8**)pChunk += sizeof(tPipe_special_data); + break; + case ePipe_chunk_ped_gib: + *(tU8**)pChunk += sizeof(tPipe_ped_gib_data); + break; + case ePipe_chunk_incident: + *(tU8**)pChunk += sizeof(tPipe_incident_data); + break; + case ePipe_chunk_spark: + *(tU8**)pChunk += sizeof(tPipe_spark_data); + break; + case ePipe_chunk_shrapnel: + *(tU8**)pChunk += (((*pChunk)->subject_index & 0x8000) ? sizeof(tPipe_shrapnel_data) : offsetof(tPipe_shrapnel_data, age)); + break; + case ePipe_chunk_screen_shake: + *(tU8**)pChunk += sizeof(tPipe_screen_shake_data); + break; + case ePipe_chunk_groove_stop: + *(tU8**)pChunk += sizeof(tPipe_groove_stop_data); + break; + case ePipe_chunk_non_car: + *(tU8**)pChunk += sizeof(tPipe_non_car_data); + break; + case ePipe_chunk_smoke: + *(tU8**)pChunk += sizeof(tPipe_smoke_data); + break; + case ePipe_chunk_oil_spill: + *(tU8**)pChunk += sizeof(tPipe_oil_spill_data); + break; + case ePipe_chunk_smoke_column: + *(tU8**)pChunk += sizeof(tPipe_smoke_column_data); + break; + case ePipe_chunk_flame: + *(tU8**)pChunk += sizeof(tPipe_flame_data); + break; + case ePipe_chunk_smudge: + *(tU8**)pChunk += offsetof(tPipe_smudge_data, vertex_changes) + (*pChunk)->chunk_data.smudge_data.vertex_count * sizeof(tSmudged_vertex); + break; + case ePipe_chunk_splash: + *(tU8**)pChunk += sizeof(tPipe_splash_data); + break; + case ePipe_chunk_prox_ray: + *(tU8**)pChunk += sizeof(tPipe_prox_ray_data); + break; + case ePipe_chunk_skid_adjustment: + *(tU8**)pChunk += sizeof(tPipe_skid_adjustment); + break; + } + *(tU8**)pChunk += offsetof(tPipe_chunk, chunk_data); + if (*(tU8**)pChunk == gEnd_of_session) { + *pChunk = old_chunk; + } else if (*(tU8**)pChunk > gEnd_of_session) { + *pChunk = old_chunk; + } } // IDA: void __usercall ApplyModelGeometry(tPipe_chunk **pChunk@) @@ -786,7 +1188,19 @@ void ApplyModelGeometry(tPipe_chunk** pChunk) { br_model* model_ptr; tCar_spec* car; LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + if (((*pChunk)->subject_index & 0xff00) == 0) { + car = &gProgram_state.current_car; + } else { + car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0xff); + } + model_ptr = car->car_model_actors[(*pChunk)->chunk_data.model_geometry_data.model_index].actor->model; + for (i = 0; i < (*pChunk)->chunk_data.model_geometry_data.vertex_count; i++) { + BrVector3Accumulate(&model_ptr->vertices[(*pChunk)->chunk_data.model_geometry_data.vertex_changes[i].vertex_index].p, + &(*pChunk)->chunk_data.model_geometry_data.vertex_changes[i].delta_coordinates); + } + SetModelForUpdate(model_ptr, car, 0); + AdvanceChunkPtr(pChunk, ePipe_chunk_model_geometry); } // IDA: void __usercall DoSmudge(tPipe_chunk **pChunk@, int pDir@) @@ -798,94 +1212,218 @@ void DoSmudge(tPipe_chunk** pChunk, int pDir) { tCar_spec* car; int group; LOG_TRACE("(%p, %d)", pChunk, pDir); - NOT_IMPLEMENTED(); + + if (((*pChunk)->subject_index & 0xff00) == 0) { + car = &gProgram_state.current_car; + } else { + car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0xff); + } + model_ptr = car->car_model_actors[(*pChunk)->chunk_data.smudge_data.model_index].actor->model; + for (i = 0; i < (*pChunk)->chunk_data.smudge_data.vertex_count; i++) { + v = (*pChunk)->chunk_data.smudge_data.vertex_changes[i].vertex_index; + inc = (*pChunk)->chunk_data.smudge_data.vertex_changes[i].light_index * pDir; + V11MODEL(model_ptr)->groups->vertex_colours[v] = ((V11MODEL(model_ptr)->groups->vertex_colours[v] >> 24) + inc) << 24; + if (model_ptr->flags & BR_MODF_UPDATEABLE) { + model_ptr->vertices[V11MODEL(model_ptr)->groups->vertex_user[v]].index = (V11MODEL(model_ptr)->groups->vertex_colours[v] >> 24) + inc; + } + } } // IDA: void __usercall ApplySmudge(tPipe_chunk **pChunk@) void ApplySmudge(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + DoSmudge(pChunk, 1); + AdvanceChunkPtr(pChunk, ePipe_chunk_smudge); } // IDA: void __usercall ApplyPedestrian(tPipe_chunk **pChunk@) void ApplyPedestrian(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + AdjustPedestrian( + (*pChunk)->subject_index, + ((*pChunk)->chunk_data.pedestrian_data.action_and_frame_index & 0x70) >> 4, + (*pChunk)->chunk_data.pedestrian_data.action_and_frame_index & 0x0f, + (*pChunk)->chunk_data.pedestrian_data.hit_points, + (*pChunk)->chunk_data.pedestrian_data.action_and_frame_index >> 7, + (*pChunk)->chunk_data.pedestrian_data.parent, + (*pChunk)->chunk_data.pedestrian_data.parent_actor, + (*pChunk)->chunk_data.pedestrian_data.spin_period, + (*pChunk)->chunk_data.pedestrian_data.jump_magnitude, + &(*pChunk)->chunk_data.pedestrian_data.offset, + &(*pChunk)->chunk_data.pedestrian_data.new_translation); + AdvanceChunkPtr(pChunk, ePipe_chunk_pedestrian); } // IDA: void __usercall ApplySpark(tPipe_chunk **pChunk@) void ApplySpark(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + AdjustSpark((*pChunk)->subject_index, + &(*pChunk)->chunk_data.spark_data.pos, + &(*pChunk)->chunk_data.spark_data.v); + AdvanceChunkPtr(pChunk, ePipe_chunk_spark); } // IDA: void __usercall ApplyShrapnel(tPipe_chunk **pChunk@) void ApplyShrapnel(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + AdjustShrapnel((*pChunk)->subject_index, + &(*pChunk)->chunk_data.shrapnel_data.pos, + (*pChunk)->chunk_data.shrapnel_data.age, + (*pChunk)->chunk_data.shrapnel_data.material); + AdvanceChunkPtr(pChunk, ePipe_chunk_shrapnel); } // IDA: void __usercall ApplyScreenWobble(tPipe_chunk **pChunk@) void ApplyScreenWobble(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + SetScreenWobble((*pChunk)->chunk_data.screen_shake_data.wobble_x, + (*pChunk)->chunk_data.screen_shake_data.wobble_y); + AdvanceChunkPtr(pChunk, ePipe_chunk_screen_shake); } // IDA: void __usercall ApplyGrooveStop(tPipe_chunk **pChunk@) void ApplyGrooveStop(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + SetGrooveInterrupt((*pChunk)->subject_index, + &(*pChunk)->chunk_data.groove_stop_data.matrix, + (*pChunk)->chunk_data.groove_stop_data.path_interrupt, + (*pChunk)->chunk_data.groove_stop_data.object_interrupt, + (*pChunk)->chunk_data.groove_stop_data.path_resumption, + (*pChunk)->chunk_data.groove_stop_data.object_resumption); + AdvanceChunkPtr(pChunk, ePipe_chunk_groove_stop); } // IDA: void __usercall ApplyNonCar(tPipe_chunk **pChunk@) void ApplyNonCar(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + AdjustNonCar((*pChunk)->chunk_data.non_car_data.actor, + &(*pChunk)->chunk_data.non_car_data.mat); + AdvanceChunkPtr(pChunk, ePipe_chunk_non_car); } // IDA: void __usercall ApplySmoke(tPipe_chunk **pChunk@) void ApplySmoke(tPipe_chunk** pChunk) { br_vector3 pos; LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + GetReducedPos(&pos, &(*pChunk)->chunk_data.smoke_data.pos); + AdjustSmoke((*pChunk)->subject_index, + (*pChunk)->chunk_data.smoke_data.type, + &pos, + (*pChunk)->chunk_data.smoke_data.radius / 1024.f, + (*pChunk)->chunk_data.smoke_data.strength / 256.f); + AdvanceChunkPtr(pChunk, ePipe_chunk_smoke); } // IDA: void __usercall ApplySmokeColumn(tPipe_chunk **pChunk@) void ApplySmokeColumn(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + AdjustSmokeColumn((*pChunk)->subject_index & 0x3fff, + ((((*pChunk)->chunk_data.smoke_column_data.car_ID) >> 8) == 0) ? &gProgram_state.current_car : GetCarSpec((*pChunk)->chunk_data.smoke_column_data.car_ID >> 8, (*pChunk)->chunk_data.smoke_column_data.car_ID & 0xff), + (*pChunk)->chunk_data.smoke_column_data.vertex, + (*pChunk)->subject_index >> 14); + AdvanceChunkPtr(pChunk, ePipe_chunk_smoke_column); } // IDA: void __usercall ApplyFlame(tPipe_chunk **pChunk@) void ApplyFlame(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + AdjustFlame((*pChunk)->subject_index, + (*pChunk)->chunk_data.flame_data.frame_count, + (*pChunk)->chunk_data.flame_data.scale_x, + (*pChunk)->chunk_data.flame_data.scale_y, + (*pChunk)->chunk_data.flame_data.offset_x, +#if DETHRACE_FIX_BUGS + (*pChunk)->chunk_data.flame_data.offset_z); +#else + (*pChunk)->chunk_data.flame_data.offset_x); +#endif + AdvanceChunkPtr(pChunk, ePipe_chunk_flame); } // IDA: void __usercall ApplySplash(tPipe_chunk **pChunk@) void ApplySplash(tPipe_chunk** pChunk) { tCar_spec* c; LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + if (((*pChunk)->subject_index & 0xff00) == 0) { + c = &gProgram_state.current_car; + } else { + c = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0xff); + } + c->water_d = (*pChunk)->chunk_data.splash_data.d; + BrVector3Copy(&c->water_normal, &(*pChunk)->chunk_data.splash_data.normal); + AdvanceChunkPtr(pChunk, ePipe_chunk_splash); } // IDA: void __usercall ApplyOilSpill(tPipe_chunk **pChunk@, tU32 pStop_time@) void ApplyOilSpill(tPipe_chunk** pChunk, tU32 pStop_time) { LOG_TRACE("(%p, %d)", pChunk, pStop_time); - NOT_IMPLEMENTED(); + + AdjustOilSpill((*pChunk)->subject_index, + &(*pChunk)->chunk_data.oil_data.mat, + (*pChunk)->chunk_data.oil_data.full_size, + (*pChunk)->chunk_data.oil_data.grow_rate, + (*pChunk)->chunk_data.oil_data.spill_time, + pStop_time, + (*pChunk)->chunk_data.oil_data.car, + &(*pChunk)->chunk_data.oil_data.original_pos, + (*pChunk)->chunk_data.oil_data.pixelmap); + AdvanceChunkPtr(pChunk, ePipe_chunk_oil_spill); } // IDA: void __usercall ApplyFrameBoundary(tPipe_chunk **pChunk@) void ApplyFrameBoundary(tPipe_chunk** pChunk) { tU32 result; LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + gLast_replay_frame_time = (*pChunk)->chunk_data.frame_boundary_data.time; + // DAT_0054b244 = PDGetTotalTime(); + AdvanceChunkPtr(pChunk, ePipe_chunk_frame_boundary); } // IDA: void __usercall ApplySound(tPipe_chunk **pChunk@) void ApplySound(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + if (!gDisable_sound) { + if ((*pChunk)->chunk_data.sound_data.volume == 0) { + DRS3StartSound2(GetOutletFromIndex((*pChunk)->chunk_data.sound_data.outlet_index), + (*pChunk)->subject_index, + 1, + -1, + -1, + 65535.f * GetReplayRate(), + 0x10000); + } else if (BrVector3LengthSquared(&(*pChunk)->chunk_data.sound_data.position) == 0) { + DRS3StartSound2(GetOutletFromIndex((*pChunk)->chunk_data.sound_data.outlet_index), + (*pChunk)->subject_index, + 1, + (*pChunk)->chunk_data.sound_data.volume & 0xff, + (*pChunk)->chunk_data.sound_data.volume >> 8, + (float)(*pChunk)->chunk_data.sound_data.pitch * fabsf(GetReplayRate()), + 0x10000); + } else { + DRS3StartSound3D(GetOutletFromIndex((*pChunk)->chunk_data.sound_data.outlet_index), + (*pChunk)->subject_index, + &(*pChunk)->chunk_data.sound_data.position, + &gZero_vector, + 1, + (*pChunk)->chunk_data.sound_data.volume, + (float)(*pChunk)->chunk_data.sound_data.pitch * fabsf(GetReplayRate()), + 0x10000); + } + } + AdvanceChunkPtr(pChunk, ePipe_chunk_sound); } // IDA: void __usercall ApplyCar(tPipe_chunk **pChunk@) @@ -894,7 +1432,35 @@ void ApplyCar(tPipe_chunk** pChunk) { br_vector3 com_offset_c; br_vector3 com_offset_w; LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + if (((*pChunk)->subject_index & 0xff00) == 0) { + car = &gProgram_state.current_car; + } else { + car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0x00ff); + } + BrMatrix34Copy(&car->car_master_actor->t.t.mat, &(*pChunk)->chunk_data.car_data.transformation); + BrVector3Copy(&car->v, &(*pChunk)->chunk_data.car_data.velocity); + BrMatrix34TApplyV(&car->velocity_car_space, &car->v, &car->car_master_actor->t.t.mat); + BrVector3InvScale(&car->velocity_car_space, &car->velocity_car_space, WORLD_SCALE); + if (BrVector3LengthSquared(&car->velocity_car_space) >= .0001f) { + BrVector3Normalise(&car->direction, &car->v); + } else { + BrVector3Negate(&car->direction, (br_vector3*)car->car_master_actor->t.t.mat.m[2]); + } + BrVector3Copy(&car->pos, &car->car_master_actor->t.t.translate.t); + BrVector3InvScale(&com_offset_c, &car->cmpos, WORLD_SCALE); + BrMatrix34ApplyV(&com_offset_w, &com_offset_c, &car->car_master_actor->t.t.mat); + BrVector3Accumulate(&car->pos, &com_offset_w); + car->speedo_speed = .07f * (*pChunk)->chunk_data.car_data.speedo_speed / 32767.f; + car->lf_sus_position = 0.15f * (*pChunk)->chunk_data.car_data.lf_sus_position / 127.f; + car->rf_sus_position = 0.15f * (*pChunk)->chunk_data.car_data.rf_sus_position / 127.f; + car->lr_sus_position = 0.15f * (*pChunk)->chunk_data.car_data.lr_sus_position / 127.f; + car->rr_sus_position = 0.15f * (*pChunk)->chunk_data.car_data.rr_sus_position / 127.f; + car->steering_angle = 60.f * (*pChunk)->chunk_data.car_data.steering_angle / 32767.f; + car->revs = 10 * ((*pChunk)->chunk_data.car_data.revs_and_gear & 0x7ff); + car->gear = ((*pChunk)->chunk_data.car_data.revs_and_gear >> 12) - 1; + car->frame_collision_flag = ((*pChunk)->chunk_data.car_data.revs_and_gear >> 11) & 0x1; + AdvanceChunkPtr(pChunk, ePipe_chunk_car); } // IDA: void __usercall ApplyDamage(tPipe_chunk **pChunk@) @@ -902,31 +1468,76 @@ void ApplyDamage(tPipe_chunk** pChunk) { tCar_spec* car; int i; LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + if (((*pChunk)->subject_index & 0xff00) == 0) { + car = &gProgram_state.current_car; + } else { + car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0x00ff); + } + for (i = 0; i < COUNT_OF(car->damage_units); i++) { + car->damage_units[i].damage_level += (*pChunk)->chunk_data.damage_data.damage_delta[i]; + } + AdvanceChunkPtr(pChunk, ePipe_chunk_damage); } // IDA: void __usercall ApplySpecial(tPipe_chunk **pChunk@) void ApplySpecial(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + switch ((*pChunk)->subject_index) { + case 0: + if (fabsf(GetReplayRate()) <= 1.f) { + FadePaletteDown(); + } + break; + case 1: + gPed_scale_factor = 2.0f; + break; + case 2: + gPed_scale_factor = 1.0f; + break; + case 3: + gPed_scale_factor = 0.5f; + break; + case 4: + gPed_scale_factor = 1.0f; + break; + } + AdvanceChunkPtr(pChunk, ePipe_chunk_special); } // IDA: void __usercall ApplyPedGib(tPipe_chunk **pChunk@) void ApplyPedGib(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + AdjustPedGib((*pChunk)->subject_index, + (*pChunk)->chunk_data.ped_gib_data.size, + (*pChunk)->chunk_data.ped_gib_data.gib_index, + (*pChunk)->chunk_data.ped_gib_data.ped_parent_index, + &(*pChunk)->chunk_data.ped_gib_data.transform); + AdvanceChunkPtr(pChunk, ePipe_chunk_ped_gib); } // IDA: void __usercall ApplyProxRay(tPipe_chunk **pChunk@) void ApplyProxRay(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + AdjustProxRay( + (*pChunk)->subject_index, + (*pChunk)->chunk_data.prox_ray_data.car_ID, + (*pChunk)->chunk_data.prox_ray_data.ped_index, + (*pChunk)->chunk_data.prox_ray_data.time); + AdvanceChunkPtr(pChunk, ePipe_chunk_prox_ray); } // IDA: void __usercall ApplySkidAdjustment(tPipe_chunk **pChunk@) void ApplySkidAdjustment(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + AdjustSkid((*pChunk)->subject_index, + &(*pChunk)->chunk_data.skid_adjustment.matrix, + (*pChunk)->chunk_data.skid_adjustment.material_index); + AdvanceChunkPtr(pChunk, ePipe_chunk_skid_adjustment); } // IDA: int __usercall ApplyPipedSession@(tU8 **pPtr@) @@ -936,19 +1547,129 @@ int ApplyPipedSession(tU8** pPtr) { tPipe_chunk* chunk_ptr; tPipe_chunk_type chunk_type; LOG_TRACE("(%p)", pPtr); - NOT_IMPLEMENTED(); + + if (*pPtr == gPipe_record_ptr) { + return 1; + } + gEnd_of_session = *pPtr + (LengthOfSession((tPipe_session *)*pPtr) - sizeof(tU16)); + REPLAY_DEBUG_ASSERT(((tPipe_session *)*pPtr)->magic1 == REPLAY_DEBUG_MAGIC1); + chunk_ptr = (tPipe_chunk *)(*pPtr + offsetof(tPipe_session, chunks)); + return_value = 0; + chunk_type = ((tPipe_session *)*pPtr)->chunk_type; + for (i = 0; i < ((tPipe_session *)*pPtr)->number_of_chunks; i++) { + switch (chunk_type) { + case ePipe_chunk_model_geometry: + ApplyModelGeometry(&chunk_ptr); + break; + case ePipe_chunk_pedestrian: + ApplyPedestrian(&chunk_ptr); + break; + case ePipe_chunk_frame_boundary: + ApplyFrameBoundary(&chunk_ptr); + return_value = 1; + break; + case ePipe_chunk_car: + ApplyCar(&chunk_ptr); + break; + case ePipe_chunk_sound: + ApplySound(&chunk_ptr); + break; + case ePipe_chunk_damage: + ApplyDamage(&chunk_ptr); + break; + case ePipe_chunk_special: + ApplySpecial(&chunk_ptr); + break; + case ePipe_chunk_ped_gib: + ApplyPedGib(&chunk_ptr); + break; + case ePipe_chunk_incident: + AdvanceChunkPtr(&chunk_ptr, ePipe_chunk_incident); + break; + case ePipe_chunk_spark: + ApplySpark(&chunk_ptr); + break; + case ePipe_chunk_shrapnel: + ApplyShrapnel(&chunk_ptr); + break; + case ePipe_chunk_screen_shake: + ApplyScreenWobble(&chunk_ptr); + break; + case ePipe_chunk_groove_stop: + ApplyGrooveStop(&chunk_ptr); + break; + case ePipe_chunk_non_car: + ApplyNonCar(&chunk_ptr); + break; + case ePipe_chunk_smoke: + ApplySmoke(&chunk_ptr); + break; + case ePipe_chunk_oil_spill: + ApplyOilSpill(&chunk_ptr, 0); + break; + case ePipe_chunk_smoke_column: + ApplySmokeColumn(&chunk_ptr); + break; + case ePipe_chunk_flame: + ApplyFlame(&chunk_ptr); + break; + case ePipe_chunk_smudge: + ApplySmudge(&chunk_ptr); + break; + case ePipe_chunk_splash: + ApplySplash(&chunk_ptr); + break; + case ePipe_chunk_prox_ray: + ApplyProxRay(&chunk_ptr); + break; + case ePipe_chunk_skid_adjustment: + ApplySkidAdjustment(&chunk_ptr); + break; + default: + break; + } + } +#if defined(DETHRACE_FIX_BUGS) + *pPtr += PIPE_ALIGN(LengthOfSession((tPipe_session *)*pPtr)); +#else + *pPtr += LengthOfSession((tPipe_session *)*pPtr); +#endif + if (*pPtr >= gPipe_buffer_working_end && *pPtr != gPipe_record_ptr) { + *pPtr = gPipe_buffer_start; + } + return return_value; } // IDA: int __usercall MoveSessionPointerBackOne@(tU8 **pPtr@) int MoveSessionPointerBackOne(tU8** pPtr) { LOG_TRACE("(%p)", pPtr); - NOT_IMPLEMENTED(); + + if (*pPtr == gPipe_buffer_oldest && *pPtr != gPipe_record_ptr) { + return 1; + } + if (*pPtr == gPipe_buffer_start) { + *pPtr = gPipe_buffer_working_end; + } + *pPtr -= sizeof(tU16); + *pPtr -= *(tU16*)*pPtr; + REPLAY_DEBUG_ASSERT(((tPipe_session*)*pPtr)->magic1 == REPLAY_DEBUG_MAGIC1); + return 0; } // IDA: int __usercall MoveSessionPointerForwardOne@(tU8 **pPtr@) int MoveSessionPointerForwardOne(tU8** pPtr) { LOG_TRACE("(%p)", pPtr); - NOT_IMPLEMENTED(); + + REPLAY_DEBUG_ASSERT(((tPipe_session*)*pPtr)->magic1 == REPLAY_DEBUG_MAGIC1); +#if defined(DETHRACE_FIX_BUGS) + *pPtr += PIPE_ALIGN(LengthOfSession((tPipe_session*)*pPtr)); +#else + *pPtr += LengthOfSession((tPipe_session*)*pPtr); +#endif + if (*pPtr >= gPipe_buffer_working_end && *pPtr != gPipe_record_ptr) { + *pPtr = gPipe_buffer_start; + } + return *pPtr == gPipe_record_ptr; } // IDA: tPipe_chunk* __usercall FindPreviousChunk@(tU8 *pPtr@, tPipe_chunk_type pType@, tChunk_subject_index pIndex@) @@ -960,7 +1681,31 @@ tPipe_chunk* FindPreviousChunk(tU8* pPtr, tPipe_chunk_type pType, tChunk_subject tPipe_chunk* mr_chunky; tChunk_subject_index masked_index; LOG_TRACE("(%p, %d, %d)", pPtr, pType, pIndex); - NOT_IMPLEMENTED(); + + ptr = pPtr; + chunk_counter = 0; + masked_index = pIndex & 0x0fff; + while (1) { + if (!MoveSessionPointerBackOne(&ptr)) { + reached_end = chunk_counter >= gMax_rewind_chunks; + chunk_counter++; + } else { + reached_end = 1; + } + if (!reached_end) { + gEnd_of_session = ptr + LengthOfSession((tPipe_session*)ptr) - sizeof(tU16); + mr_chunky = &((tPipe_session*)ptr)->chunks; + for (i = 0; i < ((tPipe_session*)ptr)->number_of_chunks && ((tPipe_session*)ptr)->chunk_type == pType; i++) { + if ((mr_chunky->subject_index & 0xfff) == masked_index) { + return mr_chunky; + } + AdvanceChunkPtr(&mr_chunky, pType); + } + } + if (reached_end) { + return NULL; + } + } } // IDA: void __usercall UndoModelGeometry(tPipe_chunk **pChunk@) @@ -969,7 +1714,20 @@ void UndoModelGeometry(tPipe_chunk** pChunk) { br_model* model_ptr; tCar_spec* car; LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + if (((*pChunk)->subject_index & 0xff00) == 0) { + car = &gProgram_state.current_car; + } else { + car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0x00ff); + } + model_ptr = car->car_model_actors[(*pChunk)->chunk_data.model_geometry_data.model_index].actor->model; + for (i = 0; i < (*pChunk)->chunk_data.model_geometry_data.vertex_count; i++) { + BrVector3Sub(&model_ptr->vertices[(*pChunk)->chunk_data.model_geometry_data.vertex_changes[i].vertex_index].p, + &model_ptr->vertices[(*pChunk)->chunk_data.model_geometry_data.vertex_changes[i].vertex_index].p, + &(*pChunk)->chunk_data.model_geometry_data.vertex_changes[i].delta_coordinates); + } + SetModelForUpdate(model_ptr, car, 0); + AdvanceChunkPtr(pChunk, ePipe_chunk_model_geometry); } // IDA: void __usercall UndoSmudge(tPipe_chunk **pChunk@) @@ -978,34 +1736,59 @@ void UndoSmudge(tPipe_chunk** pChunk) { br_model* model_ptr; tCar_spec* car; LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + DoSmudge(pChunk, -1); + AdvanceChunkPtr(pChunk, ePipe_chunk_smudge); } // IDA: void __usercall UndoPedestrian(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoPedestrian(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + temp_prev_chunk = pPrev_chunk; + if (pPrev_chunk == NULL) { + ApplyPedestrian(pChunk); + } + else { + gDisable_advance = 1; + ApplyPedestrian(&temp_prev_chunk); + gDisable_advance = 0; + AdvanceChunkPtr(pChunk, ePipe_chunk_pedestrian); + } } // IDA: void __usercall UndoFrameBoundary(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoFrameBoundary(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + ApplyFrameBoundary(pChunk); + AdvanceChunkPtr(pChunk, ePipe_chunk_frame_boundary); } // IDA: void __usercall UndoCar(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoCar(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + temp_prev_chunk = pPrev_chunk; + if (pPrev_chunk == NULL) { + ApplyCar(pChunk); + } + else { + gDisable_advance = 1; + ApplyCar(&temp_prev_chunk); + gDisable_advance = 0; + AdvanceChunkPtr(pChunk, ePipe_chunk_car); + } } // IDA: void __usercall UndoSound(tPipe_chunk **pChunk@) void UndoSound(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + AdvanceChunkPtr(pChunk,ePipe_chunk_sound); } // IDA: void __usercall UndoDamage(tPipe_chunk **pChunk@) @@ -1013,101 +1796,211 @@ void UndoDamage(tPipe_chunk** pChunk) { tCar_spec* car; int i; LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + if (((*pChunk)->subject_index & 0xff00) == 0) { + car = &gProgram_state.current_car; + } + else { + car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0xff); + } + for (i = 0; i < COUNT_OF(car->damage_units); i++) { + car->damage_units[i].damage_level -= (*pChunk)->chunk_data.damage_data.damage_delta[i]; + } + AdvanceChunkPtr(pChunk, ePipe_chunk_damage); } // IDA: void __usercall UndoSpecial(tPipe_chunk **pChunk@) void UndoSpecial(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + if ((*pChunk)->subject_index == 0) { + ApplySpecial(pChunk); + } else { + switch ((*pChunk)->subject_index) { + case 1: + gPed_scale_factor = 1.0f; + break; + case 2: + gPed_scale_factor = 2.0f; + break; + case 3: + gPed_scale_factor = 1.0f; + break; + case 4: + gPed_scale_factor = 0.5f; + break; + } + AdvanceChunkPtr(pChunk, ePipe_chunk_special); + } } // IDA: void __usercall UndoPedGib(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoPedGib(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + temp_prev_chunk = pPrev_chunk; + gDisable_advance = 1; + if (pPrev_chunk != NULL) { + ApplyPedGib(&temp_prev_chunk); + } + gDisable_advance = 0; + AdvanceChunkPtr(pChunk, ePipe_chunk_ped_gib); } // IDA: void __usercall UndoSpark(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoSpark(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + temp_prev_chunk = pPrev_chunk; + gDisable_advance = 1; + if (pPrev_chunk != NULL) { + ApplySpark(&temp_prev_chunk); + } + gDisable_advance = 0; + AdvanceChunkPtr(pChunk, ePipe_chunk_spark); } // IDA: void __usercall UndoShrapnel(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoShrapnel(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + temp_prev_chunk = pPrev_chunk; + gDisable_advance = 1; + if (pPrev_chunk != NULL) { + ApplyShrapnel(&temp_prev_chunk); + } + gDisable_advance = 0; + AdvanceChunkPtr(pChunk, ePipe_chunk_shrapnel); } // IDA: void __usercall UndoScreenWobble(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoScreenWobble(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + temp_prev_chunk = pPrev_chunk; + gDisable_advance = 1; + if (pPrev_chunk == NULL) { + SetScreenWobble(0, 0); + } + else { + ApplyScreenWobble(&temp_prev_chunk); + } + gDisable_advance = 0; + AdvanceChunkPtr(pChunk, ePipe_chunk_screen_shake); } // IDA: void __usercall UndoGrooveStop(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoGrooveStop(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + temp_prev_chunk = pPrev_chunk; + gDisable_advance = 1; + if (pPrev_chunk != NULL) { + ApplyGrooveStop(&temp_prev_chunk); + } + gDisable_advance = 0; + AdvanceChunkPtr(pChunk, ePipe_chunk_groove_stop); } // IDA: void __usercall UndoNonCar(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoNonCar(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + temp_prev_chunk = pPrev_chunk; + gDisable_advance = 1; + if (pPrev_chunk != NULL) { + ApplyNonCar(&temp_prev_chunk); + } + gDisable_advance = 0; + AdvanceChunkPtr(pChunk, ePipe_chunk_non_car); } // IDA: void __usercall UndoSmoke(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoSmoke(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + temp_prev_chunk = pPrev_chunk; + gDisable_advance = 1; + if (pPrev_chunk != NULL) { + ApplySmoke(&temp_prev_chunk); + } + gDisable_advance = 0; + AdvanceChunkPtr(pChunk, ePipe_chunk_smoke); } // IDA: void __usercall UndoSmokeColumn(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoSmokeColumn(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + ApplySmokeColumn(pChunk); } // IDA: void __usercall UndoFlame(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoFlame(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + ApplyFlame(pChunk); } // IDA: void __usercall UndoSplash(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoSplash(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + temp_prev_chunk = pPrev_chunk; + gDisable_advance = 1; + if (pPrev_chunk == NULL) { + ((((*pChunk)->subject_index & 0xff00) == 0) ? &gProgram_state.current_car : GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0xff))->water_d = 10000.f; + } + else { + ApplySplash(&temp_prev_chunk); + } + gDisable_advance = 0; + AdvanceChunkPtr(pChunk, ePipe_chunk_splash); } // IDA: void __usercall UndoOilSpill(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoOilSpill(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { tPipe_chunk* temp_prev_chunk; LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + temp_prev_chunk = pPrev_chunk; + gDisable_advance = 1; + if (pPrev_chunk != NULL) { + ApplyOilSpill(&temp_prev_chunk, (*pChunk)->chunk_data.oil_data.previous_stop_time); + } + gDisable_advance = 0; + AdvanceChunkPtr(pChunk, ePipe_chunk_oil_spill); } // IDA: void __usercall UndoProxRay(tPipe_chunk **pChunk@) void UndoProxRay(tPipe_chunk** pChunk) { LOG_TRACE("(%p)", pChunk); - NOT_IMPLEMENTED(); + + ApplyProxRay(pChunk); } // IDA: void __usercall UndoSkidAdjustment(tPipe_chunk **pChunk@, tPipe_chunk *pPrev_chunk@) void UndoSkidAdjustment(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) { LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk); - NOT_IMPLEMENTED(); + + gDisable_advance = 1; + if (pPrev_chunk == NULL) { + HideSkid((*pChunk)->subject_index); + } + else { + ApplySkidAdjustment(&pPrev_chunk); + } + gDisable_advance = 0; + AdvanceChunkPtr(pChunk, ePipe_chunk_skid_adjustment); } // IDA: int __usercall UndoPipedSession@(tU8 **pPtr@) @@ -1119,14 +2012,110 @@ int UndoPipedSession(tU8** pPtr) { int i; tPipe_chunk_type chunk_type; LOG_TRACE("(%p)", pPtr); - NOT_IMPLEMENTED(); + + if (MoveSessionPointerBackOne(pPtr)) { + return 1; + } + REPLAY_DEBUG_ASSERT(((tPipe_session*)*pPtr)->magic1 == REPLAY_DEBUG_MAGIC1); + gEnd_of_session = *pPtr + LengthOfSession((tPipe_session*)*pPtr) - sizeof(tU16); + chunk_ptr = &((tPipe_session*)*pPtr)->chunks; + chunk_type = ((tPipe_session*)*pPtr)->chunk_type; + pushed_end_of_session = gEnd_of_session; + for (i = 0; i < ((tPipe_session*)pPtr)->number_of_chunks; i++) { + if (!(chunk_type == ePipe_chunk_model_geometry || chunk_type == ePipe_chunk_sound || chunk_type == ePipe_chunk_damage || chunk_type == ePipe_chunk_special || chunk_type == ePipe_chunk_incident || chunk_type == ePipe_chunk_prox_ray || chunk_type == ePipe_chunk_smudge)) { + prev_chunk = FindPreviousChunk(*pPtr, ((tPipe_session*)*pPtr)->chunk_type, chunk_ptr->subject_index); + } + gEnd_of_session = pushed_end_of_session; + switch (chunk_type) { + case ePipe_chunk_model_geometry: + UndoModelGeometry(&chunk_ptr); + break; + case ePipe_chunk_pedestrian: + UndoPedestrian(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_frame_boundary: + UndoFrameBoundary(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_car: + UndoCar(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_sound: + UndoSound(&chunk_ptr); + break; + case ePipe_chunk_damage: + UndoDamage(&chunk_ptr); + break; + case ePipe_chunk_special: + UndoSpecial(&chunk_ptr); + break; + case ePipe_chunk_ped_gib: + UndoPedGib(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_incident: + AdvanceChunkPtr(&chunk_ptr, ePipe_chunk_incident); + break; + case ePipe_chunk_spark: + UndoSpark(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_shrapnel: + UndoShrapnel(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_screen_shake: + UndoScreenWobble(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_groove_stop: + UndoGrooveStop(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_non_car: + UndoNonCar(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_smoke: + UndoSmoke(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_oil_spill: + UndoOilSpill(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_smoke_column: + UndoSmokeColumn(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_flame: + UndoFlame(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_smudge: + UndoSmudge(&chunk_ptr); + break; + case ePipe_chunk_splash: + UndoSplash(&chunk_ptr, prev_chunk); + break; + case ePipe_chunk_prox_ray: + UndoProxRay(&chunk_ptr); + break; + case ePipe_chunk_skid_adjustment: + UndoSkidAdjustment(&chunk_ptr, prev_chunk); + break; + default: + break; + } + } + temp_ptr = *pPtr; + if (MoveSessionPointerBackOne(&temp_ptr)) { + return 1; + } + return ((tPipe_session*)temp_ptr)->chunk_type == ePipe_chunk_frame_boundary; } // IDA: tU32 __usercall FindPrevFrameTime@(tU8 *pPtr@) tU32 FindPrevFrameTime(tU8* pPtr) { tU8* temp_ptr; LOG_TRACE("(%p)", pPtr); - NOT_IMPLEMENTED(); + + temp_ptr = pPtr; + do { + if (MoveSessionPointerBackOne(&temp_ptr)) { + return 0; + } + } while (((tPipe_session*)temp_ptr)->chunk_type != ePipe_chunk_frame_boundary); + return ((tPipe_session*)temp_ptr)->chunks.chunk_data.frame_boundary_data.time; } // IDA: void __usercall ScanBuffer(tU8 **pPtr@, tPipe_chunk_type pType@, tU32 pDefault_time@, int (*pCall_back)(tPipe_chunk*, int, tU32)@, int (*pTime_check)(tU32)) @@ -1134,7 +2123,26 @@ void ScanBuffer(tU8** pPtr, tPipe_chunk_type pType, tU32 pDefault_time, int (*pC tPipe_chunk* chunk_ptr; tU32 the_time; LOG_TRACE("(%p, %d, %d, %p, %p)", pPtr, pType, pDefault_time, pCall_back, pTime_check); - NOT_IMPLEMENTED(); + + the_time = pDefault_time; + while (1) { + if (PipeSearchForwards() ? MoveSessionPointerForwardOne(pPtr) : MoveSessionPointerBackOne(pPtr)) { + return; + } + gEnd_of_session = *pPtr + LengthOfSession((tPipe_session*)*pPtr) - sizeof(tU16); + if (((tPipe_session*)*pPtr)->chunk_type == ePipe_chunk_frame_boundary) { + the_time = ((tPipe_session*)*pPtr)->chunks.chunk_data.frame_boundary_data.time; + } else if (((tPipe_session*)*pPtr)->chunk_type == pType) { + if (pCall_back(&((tPipe_session*)*pPtr)->chunks, ((tPipe_session*)*pPtr)->number_of_chunks, the_time)) { + return; + } + } + if (pTime_check != NULL) { + if (!pTime_check(the_time)) { + return; + } + } + } } // IDA: int __usercall CheckSound@(tPipe_chunk *pChunk_ptr@, int pChunk_count@, tU32 pTime@) @@ -1143,7 +2151,9 @@ int CheckSound(tPipe_chunk* pChunk_ptr, int pChunk_count, tU32 pTime) { int sound_length; tPipe_chunk* temp_ptr; LOG_TRACE("(%p, %d, %d)", pChunk_ptr, pChunk_count, pTime); - NOT_IMPLEMENTED(); + + STUB_ONCE(); + return 1; } // IDA: int __usercall SoundTimeout@(tU32 pTime@) @@ -1168,38 +2178,148 @@ int CheckCar(tPipe_chunk* pChunk_ptr, int pChunk_count, tU32 pTime) { br_vector3 difference; tPipe_chunk* temp_ptr; LOG_TRACE("(%p, %d, %d)", pChunk_ptr, pChunk_count, pTime); - NOT_IMPLEMENTED(); + + temp_ptr = pChunk_ptr; + if (PipeSearchForwards()) { + if (pTime <= gOldest_time) { + return 0; + } + } else { + if (pTime >= gOldest_time) { + return 0; + } + } + for (i = 0; i < pChunk_count; i++) { + if ((temp_ptr->subject_index & 0xff00) == 0) { + car = &gProgram_state.current_car; + } else { + car = GetCarSpec(temp_ptr->subject_index >> 8, temp_ptr->subject_index & 0xff); + } + if (car == gCar_ptr) { + BrVector3Copy(&gCar_pos, (br_vector3*)temp_ptr->chunk_data.car_data.transformation.m[3]); + BrVector3InvScale(&com_offset_c, &car->cmpos, WORLD_SCALE); + BrMatrix34ApplyV(&com_offset_w, &com_offset_c, &temp_ptr->chunk_data.car_data.transformation); + BrVector3Accumulate(&gCar_pos, &com_offset_w); + BrVector3Sub(&difference, &gCar_pos, &gReference_pos); + if (BrVector3LengthSquared(&difference) <= gMax_distance) { + gTrigger_time = pTime; + return 0; + } else { + gTrigger_time = pTime; + return 1; + } + } + AdvanceChunkPtr(&temp_ptr, ePipe_chunk_car); + } + return 0; } // IDA: int __usercall CarTimeout@(tU32 pTime@) int CarTimeout(tU32 pTime) { LOG_TRACE("(%d)", pTime); - NOT_IMPLEMENTED(); + + if (PipeSearchForwards()) { + if (pTime > gYoungest_time) { + return 0; + } + } else { + if (pTime < gYoungest_time) { + return 0; + } + } + return 1; } // IDA: void __usercall ScanCarsPositions(tCar_spec *pCar@, br_vector3 *pSource_pos@, br_scalar pMax_distance_sqr, tU32 pOffset_time, tU32 pTime_period, br_vector3 *pCar_pos, tU32 *pTime_returned) void ScanCarsPositions(tCar_spec* pCar, br_vector3* pSource_pos, br_scalar pMax_distance_sqr, tU32 pOffset_time, tU32 pTime_period, br_vector3* pCar_pos, tU32* pTime_returned) { tU8* temp_ptr; LOG_TRACE("(%p, %p, %f, %d, %d, %p, %p)", pCar, pSource_pos, pMax_distance_sqr, pOffset_time, pTime_period, pCar_pos, pTime_returned); - NOT_IMPLEMENTED(); + + temp_ptr = gPipe_play_ptr; + gTrigger_time = 0; + gMax_distance = pMax_distance_sqr; + BrVector3Copy(&gReference_pos, pSource_pos); + gCar_ptr = pCar; + + if (PipeSearchForwards()) { + gOldest_time = GetTotalTime() + pOffset_time; + gYoungest_time = gOldest_time + pTime_period; + } else { + gOldest_time = GetTotalTime() - pOffset_time; + gYoungest_time = gOldest_time - pTime_period; + } + + ScanBuffer(&temp_ptr, ePipe_chunk_car, GetTotalTime(), CheckCar, CarTimeout); + BrVector3Copy(pCar_pos, &gCar_pos); + if (pCar_pos->v[0] > 500.f) { + Vector3AddFloats(pCar_pos, pCar_pos, -1000.f, -1000.f, -1000.f); + } + *pTime_returned = gTrigger_time; } // IDA: int __usercall CheckIncident@(tPipe_chunk *pChunk_ptr@, int pChunk_count@, tU32 pTime@) int CheckIncident(tPipe_chunk* pChunk_ptr, int pChunk_count, tU32 pTime) { LOG_TRACE("(%p, %d, %d)", pChunk_ptr, pChunk_count, pTime); - NOT_IMPLEMENTED(); + + if (PipeSearchForwards()) { + if (pTime <= gOldest_time) { + return 0; + } + } else { + if (gOldest_time <= pTime) { + return 0; + } + } + gIncidentChunk = pChunk_ptr; + gTrigger_time = pTime; + return 1; } // IDA: int __usercall GetNextIncident@(tU32 pOffset_time@, tIncident_type *pIncident_type@, float *pSeverity@, tIncident_info *pInfo@, tU32 *pTime_away) int GetNextIncident(tU32 pOffset_time, tIncident_type* pIncident_type, float* pSeverity, tIncident_info* pInfo, tU32* pTime_away) { tU8* temp_ptr; LOG_TRACE("(%d, %p, %p, %p, %p)", pOffset_time, pIncident_type, pSeverity, pInfo, pTime_away); - NOT_IMPLEMENTED(); + + temp_ptr = gPipe_play_ptr; + gTrigger_time = 0; + if (PipeSearchForwards()) { + gOldest_time = GetTotalTime() + pOffset_time; + } else { + gOldest_time = GetTotalTime() - pOffset_time; + } + ScanBuffer(&temp_ptr, ePipe_chunk_incident, GetTotalTime(), CheckIncident, NULL); + if (gTrigger_time != 0) { + *pTime_away = gTrigger_time - GetTotalTime(); + *pIncident_type = gIncidentChunk->subject_index; + *pSeverity = gIncidentChunk->chunk_data.incident_data.severity; + if (*pIncident_type == eIncident_ped) { + pInfo->ped_info.ped_actor = GetPedestrianActor(gIncidentChunk->chunk_data.incident_data.info.ped_info.ped_index); + pInfo->ped_info.murderer_actor = gIncidentChunk->chunk_data.incident_data.info.ped_info.actor; + } else if (*pIncident_type == eIncident_car) { + if ((gIncidentChunk->chunk_data.incident_data.info.car_info.car_ID & 0xff00) == 0) { + pInfo->car_info.car = &gProgram_state.current_car; + } else { + pInfo->car_info.car = GetCarSpec(gIncidentChunk->chunk_data.incident_data.info.car_info.car_ID >> 8, + gIncidentChunk->chunk_data.incident_data.info.car_info.car_ID & 0xff); + } + BrVector3Copy(&pInfo->car_info.impact_point, &gIncidentChunk->chunk_data.incident_data.info.car_info.impact_point); + } else if (*pIncident_type == eIncident_wall) { + BrVector3Copy(&pInfo->wall_info.pos, &gIncidentChunk->chunk_data.incident_data.info.wall_info.pos); + } + } + return gTrigger_time; } // IDA: tU32 __cdecl GetARStartTime() tU32 GetARStartTime() { tU8* temp_ptr; LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + temp_ptr = gPipe_buffer_oldest; + do { + if (MoveSessionPointerForwardOne(&temp_ptr)) { + return 0; + } + } while (((tPipe_session*)temp_ptr)->chunk_type != ePipe_chunk_frame_boundary); + return ((tPipe_session*)temp_ptr)->chunks.chunk_data.frame_boundary_data.time; } diff --git a/src/DETHRACE/common/replay.c b/src/DETHRACE/common/replay.c index 40eecb01..1b7e3e26 100644 --- a/src/DETHRACE/common/replay.c +++ b/src/DETHRACE/common/replay.c @@ -1,9 +1,30 @@ #include "replay.h" -#include "harness/trace.h" -#include - +#include +#include "car.h" +#include "controls.h" +#include "displays.h" +#include "globvars.h" +#include "globvrpb.h" +#include "grafdata.h" #include "graphics.h" +#include "input.h" #include "loading.h" +#include "main.h" +#include "netgame.h" +#include "oil.h" +#include "opponent.h" +#include "piping.h" +#include "s3/s3.h" +#include "sys.h" +#include "utility.h" +#include "world.h" + +#include "harness/config.h" +#include "harness/os.h" +#include "harness/trace.h" + +#include +#include char* gReplay_pixie_names[10] = { "REPLAY.PIX", @@ -15,14 +36,14 @@ char* gReplay_pixie_names[10] = { "PLAY.PIX", "FFWD.PIX", "FWDEND.PIX", - "CAMERA.PIX" + "CAMERA.PIX", }; -int gSingle_frame_mode; -tU32 gCam_change_time; -int gSave_file; -int gProgress_line_left[2]; -int gProgress_line_right[2]; -int gProgress_line_top[2]; +int gSingle_frame_mode = 0; +tU32 gCam_change_time = 0; +int gSave_file = 0; +int gProgress_line_left[2] = { 70, 141 }; +int gProgress_line_right[2] = { 279, 558 }; +int gProgress_line_top[2] = { 178, 429 }; br_pixelmap* gReplay_pixies[10]; int gKey_down; int gNo_cursor; @@ -43,7 +64,7 @@ tAction_replay_camera_type gAction_replay_camera_mode; int ReplayIsPaused() { LOG_TRACE8("()"); - return gReplay_rate == 0.0; + return gReplay_rate == 0.f; } // IDA: float __cdecl GetReplayRate() @@ -56,13 +77,16 @@ float GetReplayRate() { // IDA: int __cdecl GetReplayDirection() int GetReplayDirection() { LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + return gPlay_direction; } // IDA: void __cdecl StopSaving() void StopSaving() { LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + gSave_file = 0; + gPaused = 1; } // IDA: void __usercall ActualActionReplayHeadups(int pSpecial_zappy_bastard@) @@ -74,7 +98,104 @@ void ActualActionReplayHeadups(int pSpecial_zappy_bastard) { tU16 to_play_col1; tU16 to_play_col2; LOG_TRACE("(%d)", pSpecial_zappy_bastard); - NOT_IMPLEMENTED(); + + the_time = PDGetTotalTime(); + if (gSave_file || PDKeyDown(KEY_SHIFT_ANY)) { + return; + } + if ((the_time / 400) % 2) { + DRMaskedStamp(gCurrent_graf_data->action_replay_R_x, + gCurrent_graf_data->action_replay_R_y, + gReplay_pixies[0]); + } + DRMaskedStamp(gCurrent_graf_data->action_replay_controls_x, + gCurrent_graf_data->action_replay_controls_y, + gReplay_pixies[1]); + if (pSpecial_zappy_bastard < 0) { + DRMaskedStamp(gCurrent_graf_data->action_replay_rew_start_x, + gCurrent_graf_data->action_replay_hilite_y, + gReplay_pixies[2]); + } else if (pSpecial_zappy_bastard == 0) { + if (gReplay_rate < -1.f) { + DRMaskedStamp(gCurrent_graf_data->action_replay_rew_x, + gCurrent_graf_data->action_replay_hilite_y, + gReplay_pixies[3]); + } else if (gReplay_rate > 1.f) { + DRMaskedStamp(gCurrent_graf_data->action_replay_ffwd_x, + gCurrent_graf_data->action_replay_hilite_y, + gReplay_pixies[7]); + } else if (gReplay_rate == 1.f) { + DRMaskedStamp(gCurrent_graf_data->action_replay_play_x, + gCurrent_graf_data->action_replay_hilite_y, + gReplay_pixies[6]); + } else if (gReplay_rate == -1.f) { + DRMaskedStamp(gCurrent_graf_data->action_replay_rev_play_x, + gCurrent_graf_data->action_replay_hilite_y, + gReplay_pixies[4]); + } else { + DRMaskedStamp(gCurrent_graf_data->action_replay_pause_x, + gCurrent_graf_data->action_replay_hilite_y, + gReplay_pixies[5]); + } + } else { + DRMaskedStamp(gCurrent_graf_data->action_replay_fwd_end_x, + gCurrent_graf_data->action_replay_hilite_y, + gReplay_pixies[8]); + } + + x = gProgress_line_left[gGraf_data_index] + (float)(gProgress_line_right[gGraf_data_index] - gProgress_line_left[gGraf_data_index]) * (gLast_replay_frame_time - gAction_replay_start_time) / (gAction_replay_end_time - gAction_replay_start_time); + if (x > gProgress_line_left[gGraf_data_index]) { + BrPixelmapLine(gBack_screen, + gProgress_line_left[gGraf_data_index], gProgress_line_top[gGraf_data_index], + x - 1, gProgress_line_top[gGraf_data_index], + 2); + BrPixelmapLine(gBack_screen, + gProgress_line_left[gGraf_data_index], gProgress_line_top[gGraf_data_index] + 1, + x - 1, gProgress_line_top[gGraf_data_index] + 1, + 4); + BrPixelmapLine(gBack_screen, + gProgress_line_left[gGraf_data_index], gProgress_line_top[gGraf_data_index] + 2, + x - 1, gProgress_line_top[gGraf_data_index] + 2, + 2); + } + if (x < gProgress_line_right[gGraf_data_index]) { + BrPixelmapLine(gBack_screen, + x, gProgress_line_top[gGraf_data_index], + gProgress_line_right[gGraf_data_index] - 1, gProgress_line_top[gGraf_data_index], + 81); + BrPixelmapLine(gBack_screen, + x, gProgress_line_top[gGraf_data_index] + 1, + gProgress_line_right[gGraf_data_index] - 1, gProgress_line_top[gGraf_data_index] + 1, + 82); + BrPixelmapLine(gBack_screen, + x, gProgress_line_top[gGraf_data_index] + 2, + gProgress_line_right[gGraf_data_index] - 1, gProgress_line_top[gGraf_data_index] + 2, + 81); + } + BrPixelmapLine(gBack_screen, + gProgress_line_left[gGraf_data_index] - 1, gProgress_line_top[gGraf_data_index], + gProgress_line_left[gGraf_data_index] - 1, gProgress_line_top[gGraf_data_index] + 2, + 2); + BrPixelmapLine(gBack_screen, + gProgress_line_right[gGraf_data_index], gProgress_line_top[gGraf_data_index], + gProgress_line_right[gGraf_data_index], gProgress_line_top[gGraf_data_index] + 2, + 81); + if (gCam_change_button_down) { + DRMaskedStamp(gCurrent_graf_data->action_replay_camera_x, + gCurrent_graf_data->action_replay_hilite_y, + gReplay_pixies[9]); + } + if (the_time - gCam_change_time < 2000) { + TransDRPixelmapText(gBack_screen, + gCurrent_graf_data->action_replay_cam_text_x - DRTextWidth(&gFonts[1], GetMiscString(gAction_replay_camera_mode ? 39 : 38)), + gCurrent_graf_data->action_replay_cam_text_y, + &gFonts[1], + GetMiscString(gAction_replay_camera_mode ? 39 : 38), + 2 * gCurrent_graf_data->action_replay_cam_text_x); + } + TurnOnPaletteConversion(); + DoMouseCursor(); + TurnOffPaletteConversion(); } // IDA: void __cdecl DoActionReplayPostSwap() @@ -88,13 +209,22 @@ void DoActionReplayPostSwap() { void DoZappyActionReplayHeadups(int pSpecial_zappy_bastard) { tU32 the_time; LOG_TRACE("(%d)", pSpecial_zappy_bastard); - NOT_IMPLEMENTED(); + + the_time = PDGetTotalTime(); + // Draw screen every 50ms (when we are going fast) + if (abs(pSpecial_zappy_bastard) > 10000 && the_time - gLast_replay_zappy_screen > 50) { + ActualActionReplayHeadups(pSpecial_zappy_bastard); + gLast_replay_zappy_screen = the_time; + PDScreenBufferSwap(0); + RemoveTransientBitmaps(1); + } } // IDA: void __cdecl DoActionReplayHeadups() void DoActionReplayHeadups() { LOG_TRACE("()"); - STUB_ONCE(); + + ActualActionReplayHeadups(0); } // IDA: void __usercall MoveReplayBuffer(tS32 pMove_amount@) @@ -106,27 +236,123 @@ void MoveReplayBuffer(tS32 pMove_amount) { int a; tU32 old_time; LOG_TRACE("(%d)", pMove_amount); - NOT_IMPLEMENTED(); + + old_play_ptr = NULL; + gLast_replay_zappy_screen = 0; + old_play_ptr2 = GetPipePlayPtr(); + play_ptr = old_play_ptr2; + old_time = GetTotalTime(); + for (i = 0; i < abs(pMove_amount) && play_ptr != old_play_ptr; i++) { + if (KeyIsDown(KEYMAP_ESCAPE)) { + break; + } + if (SomeReplayLeft()) { + PipingFrameReset(); + } + old_play_ptr = play_ptr; + if (pMove_amount >= 1) { + while (!ApplyPipedSession(&play_ptr)) { + DoZappyActionReplayHeadups(pMove_amount); + } + SetPipePlayPtr(play_ptr); + } else if (pMove_amount <= -1) { + while (!UndoPipedSession(&play_ptr)) { + DoZappyActionReplayHeadups(pMove_amount); + } + SetPipePlayPtr(play_ptr); + } + ProcessOilSpills(gFrame_period); + } + if (gReplay_rate < 0.f) { + CheckSound((tPipe_chunk*)old_play_ptr2, old_time, GetTotalTime()); + } + if (old_play_ptr == play_ptr) { + gReplay_rate = 0.f; + gPaused = 1; + StopSaving(); + } + if (KeyIsDown(KEYMAP_ESCAPE)) { + WaitForNoKeys(); + } } // IDA: void __cdecl MoveToEndOfReplay() void MoveToEndOfReplay() { float old_replay_rate; LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + DisablePipedSounds(); + old_replay_rate = gReplay_rate; + gReplay_rate = 100.f; + MoveReplayBuffer(INT32_MAX); + gReplay_rate = old_replay_rate; + EnablePipedSounds(); } // IDA: void __cdecl MoveToStartOfReplay() void MoveToStartOfReplay() { float old_replay_rate; LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + DisablePipedSounds(); + old_replay_rate = gReplay_rate; + gReplay_rate = -100.f; + MoveReplayBuffer(-INT32_MAX); + gReplay_rate = old_replay_rate; + EnablePipedSounds(); } // IDA: void __cdecl ToggleReplay() void ToggleReplay() { LOG_TRACE("()"); - STUB_ONCE(); + + if (!harness_game_config.enable_replay) { + NewTextHeadupSlot(4, 0, 1000, -4, "Action replay disabled (start dethrace with --enable-replay)"); + return; + } + + if (!IsActionReplayAvailable()) { + NewTextHeadupSlot(4, 0, 1000, -4, GetMiscString(37)); + return; + } + if (!gAction_replay_mode) { + if (gMap_mode) { + ToggleMap(); + } + if (gNet_mode == eNet_mode_host) { + SendGameplayToAllPlayers(eNet_gameplay_host_paused, 0, 0, 0, 0); + } + gReplay_rate = 1.f; + gPaused = 1; + gStopped_time = PDGetTotalTime(); + gPlay_direction = 1; + gAction_replay_end_time = GetTotalTime(); + gLast_replay_frame_time = gAction_replay_end_time; + gAction_replay_start_time = GetARStartTime(); + ResetPipePlayToEnd(); + LoadInterfaceStuff(1); + StartMouseCursor(); + gKey_down = KEY_KP_ENTER; + gPending_replay_rate = 0; + gCam_change_time = PDGetTotalTime(); + if (!gRace_finished) { + SaveCameraPosition(0); + } + } else { + MoveToEndOfReplay(); + EndMouseCursor(); + S3SetEffects(NULL, NULL); + UnlockInterfaceStuff(); + AddLostTime(PDGetTotalTime() - gStopped_time); + if (!gRace_finished) { + RestoreCameraPosition(0); + } + if (gNet_mode == eNet_mode_host) { + SendGameplayToAllPlayers(eNet_gameplay_host_unpaused, 0, 0, 0, 0); + } + } + gAction_replay_mode = !gAction_replay_mode; + ForceRebuildActiveCarList(); } // IDA: void __usercall ReverseSound(tS3_effect_tag pEffect_index@, tS3_sound_tag pSound_tag@) @@ -141,7 +367,19 @@ int FindUniqueFile() { FILE* f; tPath_name the_path; LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + for (index = 0; index < 1000; index++) { + PathCat(the_path, gApplication_path, "BMPFILES"); + PathCat(the_path, the_path, ""); + sprintf(&the_path[strlen(the_path)], "%03d", index); + strcat(the_path, "_0000.BMP"); + f = DRfopen(the_path, "rt"); + if (f == NULL) { + return index; + } + fclose(f); + } + return 0; } // IDA: void __usercall PollActionReplayControls(tU32 pFrame_period@) @@ -152,17 +390,193 @@ void PollActionReplayControls(tU32 pFrame_period) { int y_coord; int i; tU32 real_time; - static tU32 last_real_time; - static int psuedo_mouse_keys[8]; - static tRectangle mouse_areas[2][8]; + static tU32 last_real_time = 0; + static int psuedo_mouse_keys[8] = { + KEY_KP_7, + KEY_KP_4, + KEY_COMMA, + KEY_SPACE, + KEY_PERIOD, + KEY_KP_6, + KEY_KP_9, + KEY_KP_MULTIPLY, + }; + static tRectangle mouse_areas[2][8] = { + { + { 63, 182, 92, 198, }, + { 93, 182, 118, 198, }, + { 119, 182, 144, 198, }, + { 145, 182, 166, 198, }, + { 167, 182, 192, 198, }, + { 193, 182, 218, 198, }, + { 219, 182, 244, 198, }, + { 245, 182, 272, 198, }, + }, + { + { 126, 436, 184, 475, }, + { 186, 436, 236, 475, }, + { 238, 436, 288, 475, }, + { 290, 436, 332, 475, }, + { 334, 436, 384, 475, }, + { 386, 436, 436, 475, }, + { 438, 436, 488, 475, }, + { 490, 436, 544, 475, }, + }, + }; LOG_TRACE("(%d)", pFrame_period); - STUB_ONCE(); + + real_time = PDGetTotalTime(); + old_replay_rate = gReplay_rate; + old_key_down = gKey_down == KEY_CAPSLOCK ? -1 : gKey_down; + gKey_down = PDAnyKeyDown(); + if (KeyIsDown(KEYMAP_REPLAYMODE) && old_key_down == -1) { + ToggleReplay(); + return; + } + + if (gKey_down == -1) { + if ((old_key_down == -1 || old_key_down == KEY_KP_4 || old_key_down == KEY_KP_6 || old_key_down == KEY_KP_MULTIPLY) && EitherMouseButtonDown()) { + GetMousePosition(&x_coord, &y_coord); + for (i = 0; i < COUNT_OF(mouse_areas[0]); i++) { + if (mouse_areas[gGraf_data_index][i].left <= x_coord && mouse_areas[gGraf_data_index][i].top <= y_coord && + mouse_areas[gGraf_data_index][i].right >= x_coord && mouse_areas[gGraf_data_index][i].bottom >= y_coord) { + gKey_down = psuedo_mouse_keys[i]; + break; + } + } + } + } else { + gMouse_in_use = 0; + } + + if (gKey_down == KEY_KP_DIVIDE && old_key_down != KEY_KP_DIVIDE) { + if (gSave_file) { + StopSaving(); + } else { + gSave_bunch_ID = FindUniqueFile(); + gSave_frame_number = 0; + gSave_file = 1; + gPlay_direction = 1; + gPaused = 0; + } + } + + if (gKey_down == KEY_KP_MULTIPLY) { + gCam_change_button_down = 1; + if (old_key_down != KEY_KP_MULTIPLY) { + gCam_change_time = PDGetTotalTime(); + if (gAction_replay_camera_mode == eAction_replay_action) { + gAction_replay_camera_mode = eAction_replay_standard; + } else { + gAction_replay_camera_mode++; + } + } + } else { + gCam_change_button_down = 0; + } + + if ((gKey_down == KEY_KP_5 || gKey_down == KEY_SPACE) && old_key_down == -1) { + gPaused = !gPaused; + } else if ((gKey_down == KEY_KP_0 || gKey_down == KEY_BACKSPACE) && old_key_down == -1) { + gPlay_direction = -gPlay_direction; + if (gPaused) { + gPaused = 0; + } + } + + if (gKey_down == KEY_KP_1 && old_key_down == -1) { + gReplay_rate = -1.f; + gPlay_direction = -1; + gSingle_frame_mode = 1; + } else if (gKey_down == KEY_KP_3 && old_key_down == -1) { + gReplay_rate = 1.f; + gPlay_direction = 1; + gSingle_frame_mode = 1; + } else if (gKey_down == KEY_KP_4 || gKey_down == KEY_PAGEUP) { + if (gReplay_rate > -1.2f) { + gReplay_rate = -1.2f; + } + if (last_real_time != 0) { + gReplay_rate -= 0.002f * (real_time - last_real_time); + } + if (gReplay_rate < -8.f) { + gReplay_rate = -8.f; + } + } else if (gKey_down == KEY_KP_6 || gKey_down == KEY_PAGEDOWN) { + if (gReplay_rate < 1.2f) { + gReplay_rate = 1.2f; + } + if (last_real_time != 0) { + gReplay_rate += 0.002f * (real_time - last_real_time); + } + if (gReplay_rate > 8.f) { + gReplay_rate = 8.f; + } + } else if (gKey_down == KEY_COMMA) { + gReplay_rate = -1.f; + gPlay_direction = -1; + gPaused = 0; + } else if (gKey_down == KEY_PERIOD) { + gReplay_rate = 1.f; + gPlay_direction = 1; + gPaused = 0; + } else if (gPaused) { + gReplay_rate = 0.f; + } else { + gReplay_rate = (float)gPlay_direction; + } + + if ((gKey_down == KEY_KP_7 || gKey_down == KEY_HOME) && old_key_down == -1) { + MoveToStartOfReplay(); + gReplay_rate = 1.f; + MungeCarGraphics(gFrame_period); + GrooveThoseDelics(); + gReplay_rate = 0.f; + gPlay_direction = 1; + gPaused = 1; + } else if ((gKey_down == KEY_KP_9 || gKey_down == KEY_END) && old_key_down == -1) { + MoveToEndOfReplay(); + gReplay_rate = -1.f; + MungeCarGraphics(gFrame_period); + GrooveThoseDelics(); + gReplay_rate = 0.f; + gPlay_direction = -1; + gPaused = 1; + } + + if (gPending_replay_rate != 0.f) { + gReplay_rate = gPending_replay_rate; + } + if (old_replay_rate * gReplay_rate >= 0.f) { + gPending_replay_rate = 0.f; + } else { + gPending_replay_rate = gReplay_rate; + gReplay_rate = 0.f; + } + + if (old_replay_rate != 0.f) { + gFrame_period = gFrame_period * gReplay_rate / old_replay_rate; + } + last_real_time = fabsf(gReplay_rate) >= 1.2f ? real_time : 0; + + if (old_replay_rate <= 0.f && gReplay_rate > 0.f) { + S3SetEffects(NULL, NULL); + } else if (old_replay_rate >= 0.f && gReplay_rate < 0.f) { + S3SetEffects(ReverseSound, ReverseSound); + } } // IDA: void __cdecl CheckReplayTurnOn() void CheckReplayTurnOn() { LOG_TRACE("()"); - STUB_ONCE(); + + if (!gAction_replay_mode) { + if (!KeyIsDown(KEYMAP_REPLAYMODE) || gEntering_message) { + gKey_down = -1; + } else if (gKey_down == -1) { + ToggleReplay(); + } + } } // IDA: void __cdecl InitializeActionReplay() @@ -170,7 +584,7 @@ void InitializeActionReplay() { int i; LOG_TRACE("()"); - for (int i = 0; i < 10; i++) { + for (i = 0; i < COUNT_OF(gReplay_pixie_names); i++) { gReplay_pixies[i] = LoadPixelmap(gReplay_pixie_names[i]); } gAction_replay_camera_mode = eAction_replay_action; @@ -179,7 +593,10 @@ void InitializeActionReplay() { // IDA: void __usercall DoActionReplay(tU32 pFrame_period@) void DoActionReplay(tU32 pFrame_period) { LOG_TRACE("(%d)", pFrame_period); - NOT_IMPLEMENTED(); + + if (gReplay_rate != 0.f) { + MoveReplayBuffer((tS32)gReplay_rate); + } } // IDA: void __cdecl SynchronizeActionReplay() @@ -188,5 +605,28 @@ void SynchronizeActionReplay() { tPath_name the_path; static tU32 gLast_synch_time; LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + while (gReplay_rate != 0.f) { + if (PDGetTotalTime() - gLast_synch_time >= gFrame_period / fabsf(gReplay_rate)) { + break; + } + ServiceGameInRace(); + } + gLast_synch_time = PDGetTotalTime(); + if (gSingle_frame_mode) { + gReplay_rate = 0.f; + gSingle_frame_mode = 0; + } + + if (gSave_file) { + PathCat(the_path, gApplication_path, "BMPFILES"); + strcat(the_path, gDir_separator); + sprintf(&the_path[strlen(the_path)], "%03d_%04d.BMP", gSave_bunch_ID, gSave_frame_number); + f = DRfopen(the_path, "wb"); + if (f != NULL) { + PrintScreenFile(f); + fclose(f); + } + gSave_frame_number++; + } } diff --git a/src/DETHRACE/common/sound.c b/src/DETHRACE/common/sound.c index a7261807..04ffb0a7 100644 --- a/src/DETHRACE/common/sound.c +++ b/src/DETHRACE/common/sound.c @@ -637,7 +637,8 @@ void SetSoundVolumes() { // IDA: tS3_outlet_ptr __usercall GetOutletFromIndex@(int pIndex@) tS3_outlet_ptr GetOutletFromIndex(int pIndex) { LOG_TRACE("(%d)", pIndex); - NOT_IMPLEMENTED(); + + return gIndexed_outlets[pIndex]; } // IDA: int __usercall GetIndexFromOutlet@(tS3_outlet_ptr pOutlet@) diff --git a/src/DETHRACE/common/spark.c b/src/DETHRACE/common/spark.c index 84e15150..a8c63d4d 100644 --- a/src/DETHRACE/common/spark.c +++ b/src/DETHRACE/common/spark.c @@ -312,7 +312,28 @@ void ReplaySparks(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_ac br_vector3 tv; br_vector3 new_pos; LOG_TRACE("(%p, %p, %p, %d)", pRender_screen, pDepth_buffer, pCamera, pTime); - NOT_IMPLEMENTED(); + + for (i = 0; i < COUNT_OF(gSparks); i++) { + if (gSpark_flags & (1 << i)) { + if (gSparks[i].car == NULL) { + BrVector3Copy(&pos, &gSparks[i].pos); + } else { + BrMatrix34ApplyP(&tv, &o, &gSparks[i].car->car_master_actor->t.t.mat); + BrVector3Copy(&o, &tv); + BrMatrix34ApplyP(&pos, &gSparks[i].pos, &gSparks[i].car->car_master_actor->t.t.mat); + } + BrVector3Add(&o, &pos, &gSparks[i].length); + BrVector3Sub(&tv, &pos, (br_vector3*)gCamera_to_world.m[3]); + BrMatrix34TApplyV(&new_pos, &tv, &gCamera_to_world); + BrVector3Sub(&tv, &o, (br_vector3*)gCamera_to_world.m[3]); + BrMatrix34TApplyV(&p, &tv, &gCamera_to_world); + if (gSparks[i].colour) { + DrawLine3D(&p, &new_pos, pRender_screen, pDepth_buffer, gFog_shade_table); + } else { + DrawLine3D(&p, &new_pos, pRender_screen, pDepth_buffer, gAcid_shade_table); + } + } + } } // IDA: void __usercall RenderSparks(br_pixelmap *pRender_screen@, br_pixelmap *pDepth_buffer@, br_actor *pCamera@, br_matrix34 *pCamera_to_world@, tU32 pTime) @@ -737,7 +758,15 @@ void ReplayShrapnel(tU32 pTime) { int i; br_matrix34* mat; LOG_TRACE("(%d)", pTime); - NOT_IMPLEMENTED(); + + for (i = 0; i < COUNT_OF(gShrapnel); i++) { + mat = &gShrapnel[i].actor->t.t.mat; + if (gShrapnel_flags & (1 << i)) { + gShrapnel[i].age += GetReplayRate() * pTime; + DrMatrix34Rotate(mat, gShrapnel[i].age * BrDegreeToAngle(1), &gShrapnel[i].axis); + BrMatrix34PreShearX(mat, gShrapnel[i].shear1, gShrapnel[i].shear2); + } + } } // IDA: void __usercall MungeShrapnel(tU32 pTime@) @@ -1090,7 +1119,19 @@ void ReplaySmoke(br_pixelmap* pRender_screen, br_pixelmap* pDepth_buffer, br_act br_scalar aspect; int i; LOG_TRACE("(%p, %p, %p)", pRender_screen, pDepth_buffer, pCamera); - NOT_IMPLEMENTED(); + + for (i = 0; i < COUNT_OF(gSmoke_column); i++) { + if (gSmoke_flags & (1 << i)) { + aspect = 1.f + (gSmoke[i].radius - .05f) / .25f * .5f; + if (gSmoke[i].type & 0x10) { + SmokeCircle3D(&gSmoke[i].pos, gSmoke[i].radius / aspect, gSmoke[i].strength, 1.f, + 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); + } + } + } } // IDA: void __usercall GenerateContinuousSmoke(tCar_spec *pCar@, int wheel@, tU32 pTime@) @@ -1301,19 +1342,25 @@ void CreatePuffOfSmoke(br_vector3* pos, br_vector3* v, br_scalar strength, br_sc // IDA: void __cdecl ResetSmoke() void ResetSmoke() { LOG_TRACE("()"); - NOT_IMPLEMENTED(); + + gSmoke_flags = 0;; } // IDA: void __usercall AdjustSmoke(int pIndex@, tU8 pType@, br_vector3 *pPos@, br_scalar pRadius, br_scalar pStrength) void AdjustSmoke(int pIndex, tU8 pType, br_vector3* pPos, br_scalar pRadius, br_scalar pStrength) { LOG_TRACE("(%d, %d, %p, %f, %f)", pIndex, pType, pPos, pRadius, pStrength); - NOT_IMPLEMENTED(); + + gSmoke[pIndex].type = pType; + gSmoke[pIndex].radius = pRadius; + gSmoke[pIndex].strength = pStrength; + BrVector3Copy(&gSmoke[pIndex].pos, pPos); + gSmoke_flags |= 1 << pIndex; } // IDA: void __cdecl ActorError() void ActorError() { LOG_TRACE("()"); - NOT_IMPLEMENTED(); + } // IDA: void __usercall AdjustSmokeColumn(int pIndex@, tCar_spec *pCar@, int pVertex@, int pColour@) @@ -1321,7 +1368,27 @@ void AdjustSmokeColumn(int pIndex, tCar_spec* pCar, int pVertex, int pColour) { int i; br_actor* actor; LOG_TRACE("(%d, %p, %d, %d)", pIndex, pCar, pVertex, pColour); - NOT_IMPLEMENTED(); + + gColumn_flags ^= 1 << pIndex; + gSmoke_column[pIndex].car = pCar; + gSmoke_column[pIndex].vertex_index = pVertex; + gSmoke_column[pIndex].colour = pColour; + for (i = 0; i < COUNT_OF(gSmoke_column->frame_count); i++) { + gSmoke_column[pIndex].frame_count[i] = 100; + } + if (pColour == 0) { + if ((gColumn_flags & (1 << pIndex)) != 0) { + if (gSmoke_column[pIndex].flame_actor->depth != 0) { + ActorError(); + } + BrActorAdd(gNon_track_actor, gSmoke_column[pIndex].flame_actor); + } else { + if (gSmoke_column[pIndex].flame_actor->depth == 0) { + ActorError(); + } + BrActorRemove(gSmoke_column[pIndex].flame_actor); + } + } } // IDA: void __usercall CreateSmokeColumn(tCar_spec *pCar@, int pColour@, int pVertex_index@, tU32 pLifetime@) @@ -1431,14 +1498,38 @@ void AdjustFlame(int pIndex, int pFrame_count, br_scalar pScale_x, br_scalar pSc tSmoke_column* col; br_actor* actor; LOG_TRACE("(%d, %d, %f, %f, %f, %f)", pIndex, pFrame_count, pScale_x, pScale_y, pOffset_x, pOffset_z); - NOT_IMPLEMENTED(); + + i = pIndex >> 4; + j = pIndex & 0xf; + col = &gSmoke_column[i]; + col->frame_count[j] = pFrame_count; + col->scale_x[j] = pScale_x; + col->scale_y[j] = pScale_y; + col->offset_x[j] = pOffset_x; + col->offset_z[j] = pOffset_z; } // IDA: void __usercall ReplayFlame(tSmoke_column *col@, br_actor *actor@) void ReplayFlame(tSmoke_column* col, br_actor* actor) { int i; LOG_TRACE("(%p, %p)", col, actor); - NOT_IMPLEMENTED(); + + for (i = 0; i < COUNT_OF(col->frame_count); i++, actor = actor->next) { + col->frame_count[i] += GetReplayRate(); + if (col->frame_count[i] < 0 || col->frame_count[i] >= COUNT_OF(gFlame_map)) { + actor->type = BR_ACTOR_NONE; + } else { + actor->type = BR_ACTOR_MODEL; + actor->material->colour_map = gFlame_map[col->frame_count[i]]; + BrMaterialUpdate(actor->material, BR_MATU_ALL); + BrMatrix34Scale(&actor->t.t.mat, + col->scale_x[i] * gFlame_map[col->frame_count[i]]->width, + col->scale_y[i] * gFlame_map[col->frame_count[i]]->height, + 1.f); + actor->t.t.translate.t.v[0] = col->offset_x[i]; + actor->t.t.translate.t.v[2] = col->offset_z[i]; + } + } } // IDA: void __usercall FlameAnimate(int c@, br_vector3 *pPos@, tU32 pTime@) @@ -1540,7 +1631,15 @@ void ReplaySmokeColumn(tU32 pTime) { int i; br_vector3 dummy; LOG_TRACE("(%d)", pTime); - NOT_IMPLEMENTED(); + + for (i = 0; i < MAX_SMOKE_COLUMNS; i++) { + if ((gColumn_flags & (1 << i)) != 0) { + DoSmokeColumn(i, pTime, &dummy); + if (gSmoke_column[i].colour == 0) { + FlameAnimate(i, &gSmoke_column[i].pos, pTime); + } + } + } } // IDA: void __usercall MungeSmokeColumn(tU32 pTime@) @@ -1866,7 +1965,66 @@ void PipeInstantUnSmudge(tCar_spec* pCar) { int group; tSmudged_vertex data[1000]; LOG_TRACE("(%p)", pCar); - NOT_IMPLEMENTED(); + + if (!gAction_replay_mode) { + return; + } + actor = pCar->car_model_actors[pCar->principal_car_actor].actor; + model = actor->model; + bonny = pCar->car_model_actors[pCar->car_actor_count - 1].actor; + n = 0; + if ((model->flags & BR_MODF_KEEP_ORIGINAL) != 0 || (model->flags & BR_MODF_UPDATEABLE) != 0) { + StartPipingSession(ePipe_chunk_smudge); + j = 0; + for (group = 0; group < V11MODEL(model)->ngroups; group++) { + for (v = 0; v < V11MODEL(model)->groups[group].nvertices; v++) { + if ((V11MODEL(model)->groups[group].vertex_colours[v] >> 24) != 0) { + data[n].vertex_index = j; + data[n].light_index = -(V11MODEL(model)->groups[group].vertex_colours[v] >> 24); + n += 1; + V11MODEL(model)->groups[group].vertex_colours[v] = 0; + if ((model->flags & 0x80) != 0) { + model->vertices[V11MODEL(model)->groups[group].vertex_user[v]].index = 0; + } + if (n >= COUNT_OF(data)) { + group = V11MODEL(model)->ngroups; + break; + } + } + j += 1; + } + } + if (n != 0) { + AddSmudgeToPipingSession(pCar->car_ID, pCar->principal_car_actor, n, data); + } + if (bonny != actor) { + b_model = bonny->model; + n = 0; + j = 0; + for (group = 0; group < V11MODEL(model)->ngroups; group++) { + for (v = 0; v < V11MODEL(model)->groups[group].nvertices; v++) { + if ((V11MODEL(model)->groups[group].vertex_colours[v] >> 24) != 0) { + data[n].vertex_index = j; + data[n].light_index = -V11MODEL(model)->groups[group].nvertices; + n += 1; + V11MODEL(model)->groups[group].vertex_colours[v] = 0; + if ((b_model->flags & BR_MODF_UPDATEABLE) != 0) { + b_model->vertices[V11MODEL(model)->groups[group].vertex_user[v]].index = 0; + } + if (n >= COUNT_OF(data)) { + group = V11MODEL(model)->groups[group].nvertices; + break; + } + } + j += 1; + } + } + if (n != 0) { + AddSmudgeToPipingSession(pCar->car_ID, pCar->car_actor_count - 1, n, data); + } + } + EndPipingSession(); + } } // IDA: void __usercall SmudgeCar(tCar_spec *pCar@, int fire_point@) diff --git a/src/DETHRACE/constants.h b/src/DETHRACE/constants.h index 3498ad85..822c6cd5 100644 --- a/src/DETHRACE/constants.h +++ b/src/DETHRACE/constants.h @@ -305,6 +305,7 @@ typedef enum keymapcodes { KEYMAP_SEND_MESSAGE = 66, // default ` KEYMAP_ARROW = 25, // default R KEYMAP_INFO = 26, // default I + KEYMAP_REPLAYMODE = 57, // default KP_ENTER } keymapcodes; enum { diff --git a/src/DETHRACE/dr_types.h b/src/DETHRACE/dr_types.h index 9b9698f5..9238cad9 100644 --- a/src/DETHRACE/dr_types.h +++ b/src/DETHRACE/dr_types.h @@ -74,7 +74,7 @@ typedef struct tCar_spec_struct2 tCar_spec2; typedef struct tPath_node_struct tPath_node; typedef struct tPath_section_struct tPath_section; typedef tU32 tPlayer_ID; -typedef void* tPipe_reset_proc(void); +typedef void tPipe_reset_proc(void); typedef struct tPowerup tPowerup; #ifdef DETHRACE_FIX_BUGS typedef int tGot_proc(tPowerup*, tCar_spec*); @@ -2700,6 +2700,9 @@ typedef struct tPipe_chunk { // size: 0x58 typedef struct tPipe_session { tPipe_chunk_type chunk_type; tU8 number_of_chunks; +#if defined(DETHRACE_REPLAY_DEBUG) + int magic1; +#endif tPipe_chunk chunks; } tPipe_session; diff --git a/src/DETHRACE/macros.h b/src/DETHRACE/macros.h index 79974dab..5e961d87 100644 --- a/src/DETHRACE/macros.h +++ b/src/DETHRACE/macros.h @@ -39,7 +39,8 @@ #define Vector3EqualElements(V, A, B, C) \ ((V)->v[0] == (A) && (V)->v[1] == (B) && (V)->v[2] == (C)) #define Vector3IsZero(V) Vector3EqualElements((V), 0.f, 0.f, 0.f) - +#define Vector3AddFloats(V1, V2, X, Y, Z) \ + do { (V1)->v[0] = (V2)->v[0] + (X); (V1)->v[1] = (V2)->v[1] + (Y); (V1)->v[2] = (V2)->v[2] + (Z); } while (0) #define SwapValuesUsingTemporary(V1, V2, T) \ do { \ (T) = (V1); \ @@ -47,4 +48,4 @@ (V2) = (T); \ } while (0) -#endif \ No newline at end of file +#endif diff --git a/src/DETHRACE/pc-dos/dossys.c b/src/DETHRACE/pc-dos/dossys.c index d40a3f27..ff177f37 100644 --- a/src/DETHRACE/pc-dos/dossys.c +++ b/src/DETHRACE/pc-dos/dossys.c @@ -264,7 +264,7 @@ void PDFatalError(char* pThe_str) { // wait for keypress - exit(1); + abort(); } // IDA: void __usercall PDNonFatalError(char *pThe_str@) @@ -595,13 +595,13 @@ void PDAllocateActionReplayBuffer(char** pBuffer, tU32* pBuffer_size) { tU32 lba; tU32 required; LOG_TRACE("(%p, %p)", pBuffer, pBuffer_size); - NOT_IMPLEMENTED(); + + OS_AllocateActionReplayBuffer(pBuffer, pBuffer_size); } // IDA: void __usercall PDDisposeActionReplayBuffer(char *pBuffer@) void PDDisposeActionReplayBuffer(char* pBuffer) { LOG_TRACE("(\"%s\")", pBuffer); - NOT_IMPLEMENTED(); } // IDA: void __usercall Usage(char *pProgpath@) diff --git a/src/S3/include/s3/s3.h b/src/S3/include/s3/s3.h index 3bda04c5..4abdd319 100644 --- a/src/S3/include/s3/s3.h +++ b/src/S3/include/s3/s3.h @@ -60,4 +60,6 @@ void S3BindListenerLeftBRender(br_vector3* left); int S3IsCDAPlaying2(); +int S3SetEffects(tS3_sample_filter* filter1, tS3_sample_filter* filter2); + #endif diff --git a/src/S3/s3sound.c b/src/S3/s3sound.c index 11d51d2c..7b2f4bad 100644 --- a/src/S3/s3sound.c +++ b/src/S3/s3sound.c @@ -3,6 +3,7 @@ #include "harness/config.h" #include "miniaudio/miniaudio.h" #include "resource.h" +#include "harness/trace.h" #include #include #include @@ -356,4 +357,8 @@ int S3SyncSampleRate(tS3_channel* chan) { } } return 1; -} \ No newline at end of file +} + +void S3SetEffects(tS3_sample_filter* filter1, tS3_sample_filter* filter2) { + STUB_ONCE(); +} diff --git a/src/harness/harness.c b/src/harness/harness.c index 11bbc9bd..4ff2d719 100644 --- a/src/harness/harness.c +++ b/src/harness/harness.c @@ -195,6 +195,8 @@ void Harness_Init(int* argc, char* argv[]) { harness_game_config.volume_multiplier = 1.0f; // start window in windowed mode harness_game_config.start_full_screen = 0; + // disable replay by default + harness_game_config.enable_replay = 0; // install signal handler by default harness_game_config.install_signalhandler = 1; @@ -284,6 +286,9 @@ int Harness_ProcessCommandLine(int* argc, char* argv[]) { } else if (strcasecmp(argv[i], "--full-screen") == 0) { harness_game_config.start_full_screen = 1; handled = 1; + } else if (strcasecmp(argv[i], "--enable-replay") == 0) { + harness_game_config.enable_replay = 1; + handled = 1; } if (handled) { diff --git a/src/harness/include/harness/config.h b/src/harness/include/harness/config.h index 1240e3aa..41174225 100644 --- a/src/harness/include/harness/config.h +++ b/src/harness/include/harness/config.h @@ -39,6 +39,7 @@ typedef struct tHarness_game_config { int enable_diagnostics; float volume_multiplier; int start_full_screen; + int enable_replay; int install_signalhandler; } tHarness_game_config; diff --git a/src/harness/include/harness/os.h b/src/harness/include/harness/os.h index 196032e9..8514abaa 100644 --- a/src/harness/include/harness/os.h +++ b/src/harness/include/harness/os.h @@ -46,4 +46,7 @@ void OS_InstallSignalHandler(char* program_name); FILE* OS_fopen(const char* pathname, const char* mode); +// Required: return a buffer for action replay. Preferably 20MB. If that is not available, then allocate a buffer of 12MB, 6MB, 4MB, 500kB or 64kiB +void OS_AllocateActionReplayBuffer(char** pBuffer, unsigned* pBuffer_size); + #endif diff --git a/src/harness/os/linux.c b/src/harness/os/linux.c index 713deeee..a6596134 100644 --- a/src/harness/os/linux.c +++ b/src/harness/os/linux.c @@ -23,6 +23,8 @@ #include #include +#define ARRAY_SIZE(A) (sizeof(A) / sizeof(A[0])) + static int stack_nbr = 0; static char _program_name[1024]; #define MAX_STACK_FRAMES 64 @@ -335,3 +337,31 @@ FILE* OS_fopen(const char* pathname, const char* mode) { } return NULL; } + +void OS_AllocateActionReplayBuffer(char** pBuffer, unsigned* pBuffer_size) { + static int allocated = 0; + static char* buffer = NULL; + static unsigned buffer_size = 0; + unsigned i; + const int wanted_sizes[] = { + 20000000, + 16000000, + 6000000, + 4000000, + 500000, + }; + + if (!allocated) { + allocated = 1; + buffer_size = 0; + for (i = 0; i < ARRAY_SIZE(wanted_sizes); i++) { + buffer = malloc(wanted_sizes[i]); + if (buffer != NULL) { + buffer_size = wanted_sizes[i]; + break; + } + } + } + *pBuffer = buffer; + *pBuffer_size = buffer_size; +} diff --git a/src/harness/os/macos.c b/src/harness/os/macos.c index 1d1db302..31e8fbd3 100644 --- a/src/harness/os/macos.c +++ b/src/harness/os/macos.c @@ -19,6 +19,8 @@ #include #include +#define ARRAY_SIZE(A) (sizeof(A) / sizeof(A[0])) + static int stack_nbr = 0; static char _program_name[1024]; #define MAX_STACK_FRAMES 64 @@ -294,3 +296,31 @@ void OS_InstallSignalHandler(char* program_name) { FILE* OS_fopen(const char* pathname, const char* mode) { return fopen(pathname, mode); } + +void OS_AllocateActionReplayBuffer(char** pBuffer, unsigned* pBuffer_size) { + static int allocated = 0; + static char* buffer = NULL; + static unsigned buffer_size = 0; + unsigned i; + const int wanted_sizes[] = { + 20000000, + 16000000, + 6000000, + 4000000, + 500000, + }; + + if (!allocated) { + allocated = 1; + buffer_size = 0; + for (i = 0; i < ARRAY_SIZE(wanted_sizes); i++) { + buffer = malloc(wanted_sizes[i]); + if (buffer != NULL) { + buffer_size = wanted_sizes[i]; + break; + } + } + } + *pBuffer = buffer; + *pBuffer_size = buffer_size; +} diff --git a/src/harness/os/windows.c b/src/harness/os/windows.c index 64c60e7b..357e5786 100644 --- a/src/harness/os/windows.c +++ b/src/harness/os/windows.c @@ -6,6 +6,7 @@ #include #include "harness/os.h" + #include #include #include @@ -23,6 +24,8 @@ #define Ebp Rbp #endif +void dr_dprintf(char* fmt_string, ...); + static int stack_nbr = 0; static char _program_name[1024]; LARGE_INTEGER qpc_start_time, EndingTime, ElapsedMicroseconds; @@ -216,3 +219,92 @@ FILE* OS_fopen(const char* pathname, const char* mode) { return f; } + +void OS_AllocateActionReplayBuffer(char** pBuffer, unsigned* pBuffer_size) { + static int allocated = 0; + static char* buffer = NULL; + static unsigned buffer_size = 0; + MEMORYSTATUS memory_status; + unsigned wanted; + + if (allocated) { + *pBuffer = buffer; + *pBuffer_size = buffer_size; + return; + } + allocated = 1; + buffer = NULL; + memory_status.dwLength = sizeof(memory_status); + GlobalMemoryStatus(&memory_status); + dr_dprintf( + "Win32AllocateActionReplayBuffer(): Memory Status BEFORE Action Replay Allocation:\n" + " dwLength %u\n" + " dwMemoryLoad %u\n" + " dwTotalPhys %u\n" + " dwAvailPhys %u\n" + " dwTotalPageFile %u\n" + " dwAvailPageFile %u\n" + " dwTotalVirtual %u\n" + " dwAvailVirtual %u", + memory_status.dwLength, + memory_status.dwMemoryLoad, + memory_status.dwTotalPhys, + memory_status.dwAvailPhys, + memory_status.dwTotalPageFile, + memory_status.dwAvailPageFile, + memory_status.dwTotalVirtual, + memory_status.dwAvailVirtual); + wanted = 20000000; + if (memory_status.dwTotalPhys < 16000000) { + wanted = 500000; + } else if (memory_status.dwTotalPhys < 24000000) { + wanted = 4000000; + } else if (memory_status.dwTotalPhys < 32000000) { + wanted = 6000000; + } else if (memory_status.dwTotalPhys < 48000000) { + wanted = 12000000; + } + dr_dprintf("Win32AllocateActionReplayBuffer(): We want %d bytes...", wanted); + if (memory_status.dwAvailPageFile + memory_status.dwAvailPhys < wanted) { + wanted = (memory_status.dwAvailPageFile + memory_status.dwAvailPhys) - 0x100000; + dr_dprintf("Win32AllocateActionReplayBuffer(): ...but there's only %d bytes available...", wanted); + } + if (wanted < 0x10000) { + wanted = 0x10000; + dr_dprintf("Win32AllocateActionReplayBuffer(): ...but we have to have a minimum size of %d bytes...", wanted); + } + while (wanted >= 0x10000) { + buffer = malloc(wanted); + if (buffer != NULL) { + break; + } + } + if (buffer == NULL) { + buffer_size = 0; + } else { + buffer_size = wanted; + // Sleep(1000); // Commented out 1s sleep + } + dr_dprintf("Win32AllocateActionReplayBuffer(): Actually allocated %d bytes.", wanted); + GlobalMemoryStatus(&memory_status); + dr_dprintf( + "Win32AllocateActionReplayBuffer(): Memory Status AFTER Action Replay Allocation:\n" + " dwLength %u\n" + " dwMemoryLoad %u\n" + " dwTotalPhys %u\n" + " dwAvailPhys %u\n" + " dwTotalPageFile %u\n" + " dwAvailPageFile %u\n" + " dwTotalVirtual %u\n" + " dwAvailVirtual %u", + memory_status.dwLength, + memory_status.dwMemoryLoad, + memory_status.dwTotalPhys, + memory_status.dwAvailPhys, + memory_status.dwTotalPageFile, + memory_status.dwAvailPageFile, + memory_status.dwTotalVirtual, + memory_status.dwAvailVirtual); + *pBuffer = buffer; + *pBuffer_size = buffer_size; +}