Subversion Repositories Games.Carmageddon

Rev

Rev 18 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include "piping.h"
  2. #include "brender/brender.h"
  3. #include "car.h"
  4. #include "crush.h"
  5. #include "errors.h"
  6. #include "globvars.h"
  7. #include "globvrpb.h"
  8. #include "graphics.h"
  9. #include "harness/trace.h"
  10. #include "oil.h"
  11. #include "opponent.h"
  12. #include "pedestrn.h"
  13. #include "replay.h"
  14. #include "skidmark.h"
  15. #include "sound.h"
  16. #include "spark.h"
  17. #include "sys.h"
  18. #include "utility.h"
  19. #include "world.h"
  20. #include <stdlib.h>
  21. #include <string.h>
  22.  
  23. tU8* gPipe_buffer_start = NULL;
  24. int gDisable_sound = 0;
  25. int gDisable_advance = 0;
  26. int gMax_rewind_chunks = 1000;
  27. float gWall_severity = 0.f;
  28. tPipe_reset_proc* gReset_procs[32] = {
  29.     NULL,
  30.     NULL,
  31.     NULL,
  32.     NULL,
  33.     NULL,
  34.     NULL,
  35.     NULL,
  36.     NULL,
  37.     NULL,
  38.     NULL,
  39.     NULL,
  40.     ResetAllPedestrians,
  41.     NULL,
  42.     NULL,
  43.     NULL,
  44.     NULL,
  45.     NULL,
  46.     ResetAllPedGibs,
  47.     NULL,
  48.     ResetSparks,
  49.     ResetShrapnel,
  50.     ResetScreenWobble,
  51.     NULL,
  52.     NULL,
  53.     ResetSmoke,
  54.     NULL,
  55.     NULL,
  56.     NULL,
  57.     NULL,
  58.     NULL,
  59.     ResetProxRay,
  60.     NULL,
  61. };
  62. tPiped_registration_snapshot gRegistration_snapshots[5];
  63. tPipe_smudge_data* gSmudge_space;
  64. tU32 gOldest_time;
  65. int gCurrent_snapshot_registration_index;
  66. tPipe_chunk* gMr_chunky;
  67. tCar_spec* gCar_ptr;
  68. br_vector3 gZero_vector;
  69. tPipe_chunk_type gReentrancy_array[5];
  70. tU32 gLast_time;
  71. tPipe_model_geometry_data* gModel_geometry_space;
  72. tU32 gEnd_time;
  73. tU32 gTrigger_time;
  74. int gReentrancy_count;
  75. br_vector3 gCar_pos;
  76. br_vector3 gReference_pos;
  77. br_scalar gMax_distance;
  78. tU32 gLoop_abort_time;
  79. br_vector3 gWall_impact_point;
  80. tU8* gPipe_buffer_working_end;
  81. tU32 gYoungest_time;
  82. tU8* gPipe_buffer_phys_end;
  83. tU8* gLocal_buffer_record_ptr;
  84. tU8* gPipe_play_ptr;
  85. tU8* gEnd_of_session;
  86. tU8* gPipe_record_ptr;
  87. tU8* gPipe_buffer_oldest;
  88. tU32 gPipe_buffer_size;
  89. tU8* gLocal_buffer;
  90. tU32 gLocal_buffer_size;
  91. tPipe_chunk* gIncidentChunk; // FIXME: added by DethRace (really needed?)
  92.  
  93. #define LOCAL_BUFFER_SIZE 15000
  94.  
  95. #if defined(DETHRACE_REPLAY_DEBUG)
  96. #define REPLAY_DEBUG_CHUNK_MAGIC1 0x1ed6ef85
  97. #define REPLAY_DEBUG_SESSION_MAGIC1 0x617bbc04
  98. #define REPLAY_DEBUG_ASSERT(test) assert(test)
  99. #include <assert.h>
  100. #else
  101. #define REPLAY_DEBUG_ASSERT(test)
  102. #endif
  103.  
  104. #if defined(DETHRACE_FIX_BUGS)
  105. #define PIPE_ALIGN(V) (((V) + sizeof(void*) - 1) & ~(sizeof(void*) - 1))
  106. #endif
  107.  
  108. // IDA: void __usercall GetReducedPos(br_vector3 *v@<EAX>, tReduced_pos *p@<EDX>)
  109. void GetReducedPos(br_vector3* v, tReduced_pos* p) {
  110.     LOG_TRACE("(%p, %p)", v, p);
  111.  
  112.     v->v[0] = p->v[0] / 800.f;
  113.     v->v[1] = p->v[1] / 800.f;
  114.     v->v[2] = p->v[2] / 800.f;
  115.     BrVector3Accumulate(v, &gProgram_state.current_car.car_master_actor->t.t.translate.t);
  116. }
  117.  
  118. // IDA: void __usercall SaveReducedPos(tReduced_pos *p@<EAX>, br_vector3 *v@<EDX>)
  119. void SaveReducedPos(tReduced_pos* p, br_vector3* v) {
  120.     br_vector3 tv;
  121.     LOG_TRACE("(%p, %p)", p, v);
  122.  
  123.     BrVector3Sub(&tv, v, &gProgram_state.current_car.car_master_actor->t.t.translate.t);
  124.     p->v[0] = tv.v[0] * 800.f;
  125.     p->v[1] = tv.v[1] * 800.f;
  126.     p->v[2] = tv.v[2] * 800.f;
  127. }
  128.  
  129. // IDA: int __cdecl PipeSearchForwards()
  130. int PipeSearchForwards(void) {
  131.     LOG_TRACE("()");
  132.  
  133.     if (gPipe_play_ptr == gPipe_record_ptr) {
  134.         return 0;
  135.     }
  136.     if (gPipe_play_ptr == gPipe_buffer_oldest) {
  137.         return 1;
  138.     }
  139.     if (GetReplayRate() == 0.f) {
  140.         return GetReplayDirection() > 0;
  141.     } else {
  142.         return GetReplayRate() > 0.f;
  143.     }
  144. }
  145.  
  146. // IDA: int __cdecl IsActionReplayAvailable()
  147. int IsActionReplayAvailable(void) {
  148.     LOG_TRACE("()");
  149.  
  150.     return gPipe_buffer_start != NULL;
  151. }
  152.  
  153. // IDA: int __cdecl SomeReplayLeft()
  154. int SomeReplayLeft(void) {
  155.     LOG_TRACE("()");
  156.  
  157.     return ((GetReplayDirection() >= 1 && gPipe_play_ptr != gPipe_record_ptr) || (GetReplayDirection() <= -1 && gPipe_play_ptr != gPipe_buffer_oldest));
  158. }
  159.  
  160. // IDA: void __cdecl DisablePipedSounds()
  161. void DisablePipedSounds(void) {
  162.     LOG_TRACE("()");
  163.  
  164.     gDisable_sound = 1;
  165. }
  166.  
  167. // IDA: void __cdecl EnablePipedSounds()
  168. void EnablePipedSounds(void) {
  169.     LOG_TRACE("()");
  170.  
  171.     gDisable_sound = 0;
  172. }
  173.  
  174. // IDA: tU32 __usercall LengthOfSession@<EAX>(tPipe_session *pSession@<EAX>)
  175. tU32 LengthOfSession(tPipe_session* pSession) {
  176.     int i;
  177.     tU32 running_total;
  178.     tPipe_chunk* the_chunk;
  179.     LOG_TRACE("(%p)", pSession);
  180.  
  181. #define SIZEOF_CHUNK(MEMBER) (offsetof(tPipe_chunk, chunk_data) + sizeof(pSession->chunks.chunk_data.MEMBER))
  182. #define ROUND_UP(V, M) (((V) + (M)-1) & (~((M)-1)))
  183.  
  184.     REPLAY_DEBUG_ASSERT(pSession->pipe_magic1 == REPLAY_DEBUG_SESSION_MAGIC1);
  185.  
  186.     switch (pSession->chunk_type) {
  187.     case ePipe_chunk_actor_rstyle:
  188.         running_total = SIZEOF_CHUNK(actor_rstyle_data) * pSession->number_of_chunks;
  189.         break;
  190.     case ePipe_chunk_actor_translate:
  191.         running_total = SIZEOF_CHUNK(actor_translate_data) * pSession->number_of_chunks;
  192.         break;
  193.     case ePipe_chunk_actor_transform:
  194.         running_total = SIZEOF_CHUNK(actor_transform_data) * pSession->number_of_chunks;
  195.         break;
  196.     case ePipe_chunk_actor_create:
  197.         running_total = 0;
  198.         break;
  199.     case ePipe_chunk_actor_destroy:
  200.         running_total = 0;
  201.         break;
  202.     case ePipe_chunk_actor_relink:
  203.         running_total = SIZEOF_CHUNK(actor_relink_data) * pSession->number_of_chunks;
  204.         break;
  205.     case ePipe_chunk_actor_material:
  206.         running_total = SIZEOF_CHUNK(actor_material_data) * pSession->number_of_chunks;
  207.         break;
  208.     case ePipe_chunk_face_material:
  209.         running_total = SIZEOF_CHUNK(face_material_data) * pSession->number_of_chunks;
  210.         break;
  211.     case ePipe_chunk_material_trans:
  212.         running_total = SIZEOF_CHUNK(material_trans_data) * pSession->number_of_chunks;
  213.         break;
  214.     case ePipe_chunk_material_pixelmap:
  215.         running_total = SIZEOF_CHUNK(material_pixelmap_data) * pSession->number_of_chunks;
  216.         break;
  217.     case ePipe_chunk_model_geometry:
  218.         running_total = 0;
  219.         for (i = 0; i < pSession->number_of_chunks; i++) {
  220.             the_chunk = (tPipe_chunk*)&((tU8*)&pSession->chunks)[running_total];
  221.             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);
  222.         }
  223.         break;
  224.     case ePipe_chunk_pedestrian:
  225.         running_total = 0;
  226.         for (i = 0; i < pSession->number_of_chunks; i++) {
  227.             the_chunk = (tPipe_chunk*)&(((tU8*)&pSession->chunks)[running_total]);
  228.             if (the_chunk->chunk_data.pedestrian_data.hit_points <= 0) {
  229.                 running_total += SIZEOF_CHUNK(pedestrian_data);
  230.             } else {
  231.                 running_total += offsetof(tPipe_pedestrian_data, spin_period) + offsetof(tPipe_chunk, chunk_data);
  232.             }
  233.         }
  234.         break;
  235.     case ePipe_chunk_frame_boundary:
  236.         running_total = SIZEOF_CHUNK(frame_boundary_data);
  237.         break;
  238.     case ePipe_chunk_car:
  239.         running_total = SIZEOF_CHUNK(car_data) * pSession->number_of_chunks;
  240.         break;
  241.     case ePipe_chunk_sound:
  242.         running_total = SIZEOF_CHUNK(sound_data) * pSession->number_of_chunks;
  243.         break;
  244.     case ePipe_chunk_damage:
  245.         running_total = SIZEOF_CHUNK(damage_data);
  246.         break;
  247.     case ePipe_chunk_special:
  248.         running_total = SIZEOF_CHUNK(special_data) * pSession->number_of_chunks;
  249.         break;
  250.     case ePipe_chunk_ped_gib:
  251.         running_total = SIZEOF_CHUNK(ped_gib_data) * pSession->number_of_chunks;
  252.         break;
  253.     case ePipe_chunk_incident:
  254.         running_total = SIZEOF_CHUNK(incident_data) * pSession->number_of_chunks;
  255.         break;
  256.     case ePipe_chunk_spark:
  257.         running_total = SIZEOF_CHUNK(spark_data) * pSession->number_of_chunks;
  258.         break;
  259.     case ePipe_chunk_shrapnel:
  260.         running_total = 0;
  261.         for (i = 0; i < pSession->number_of_chunks; i++) {
  262.             the_chunk = (tPipe_chunk*)&((tU8*)&pSession->chunks)[running_total];
  263.             if (the_chunk->subject_index & 0x8000) {
  264.                 running_total += SIZEOF_CHUNK(shrapnel_data);
  265.             } else {
  266.                 running_total += offsetof(tPipe_shrapnel_data, age) + offsetof(tPipe_chunk, chunk_data);
  267.             }
  268.         }
  269.         break;
  270.     case ePipe_chunk_screen_shake:
  271.         running_total = SIZEOF_CHUNK(screen_shake_data) * pSession->number_of_chunks;
  272.         break;
  273.     case ePipe_chunk_groove_stop:
  274.         running_total = SIZEOF_CHUNK(groove_stop_data) * pSession->number_of_chunks;
  275.         break;
  276.     case ePipe_chunk_non_car:
  277.         running_total = SIZEOF_CHUNK(non_car_data) * pSession->number_of_chunks;
  278.         break;
  279.     case ePipe_chunk_smoke:
  280.         running_total = SIZEOF_CHUNK(smoke_data) * pSession->number_of_chunks;
  281.         break;
  282.     case ePipe_chunk_oil_spill:
  283.         running_total = SIZEOF_CHUNK(oil_data) * pSession->number_of_chunks;
  284.         break;
  285.     case ePipe_chunk_smoke_column:
  286.         running_total = ROUND_UP(SIZEOF_CHUNK(smoke_column_data), 4) * pSession->number_of_chunks;
  287.         break;
  288.     case ePipe_chunk_flame:
  289.         running_total = SIZEOF_CHUNK(flame_data) * pSession->number_of_chunks;
  290.         break;
  291.     case ePipe_chunk_smudge:
  292.         running_total = 0;
  293.         for (i = 0; i < pSession->number_of_chunks; i++) {
  294.             the_chunk = (tPipe_chunk*)&((tU8*)&pSession->chunks)[running_total];
  295.             running_total += the_chunk->chunk_data.smudge_data.vertex_count * sizeof(tSmudged_vertex) + offsetof(tPipe_smudge_data, vertex_changes) + offsetof(tPipe_chunk, chunk_data);
  296.         }
  297.         break;
  298.     case ePipe_chunk_splash:
  299.         running_total = SIZEOF_CHUNK(splash_data) * pSession->number_of_chunks;
  300.         break;
  301.     case ePipe_chunk_prox_ray:
  302.         running_total = SIZEOF_CHUNK(prox_ray_data) * pSession->number_of_chunks;
  303.         break;
  304.     case ePipe_chunk_skid_adjustment:
  305.         running_total = SIZEOF_CHUNK(skid_adjustment) * pSession->number_of_chunks;
  306.         break;
  307.     default:
  308.         running_total = 0;
  309.         break;
  310.     }
  311.     running_total += offsetof(tPipe_session, chunks) + sizeof(tU16);
  312.     if (running_total % 2 != 0) {
  313.         FatalError(kFatalError_PipingSystem);
  314.     }
  315.     return running_total;
  316. }
  317.  
  318. // IDA: void __usercall StartPipingSession2(tPipe_chunk_type pThe_type@<EAX>, int pMunge_reentrancy@<EDX>)
  319. void StartPipingSession2(tPipe_chunk_type pThe_type, int pMunge_reentrancy) {
  320.     LOG_TRACE("(%d, %d)", pThe_type, pMunge_reentrancy);
  321.  
  322.     if (gPipe_buffer_start != NULL && !gAction_replay_mode && gProgram_state.racing) {
  323.         if (pMunge_reentrancy) {
  324.             if (gReentrancy_count != 0) {
  325.                 gReentrancy_array[gReentrancy_count - 1] = ((tPipe_session*)gLocal_buffer)->chunk_type;
  326.                 EndPipingSession2(0);
  327.             }
  328.             gReentrancy_count++;
  329.         }
  330.         ((tPipe_session*)gLocal_buffer)->chunk_type = pThe_type;
  331.         ((tPipe_session*)gLocal_buffer)->number_of_chunks = 0;
  332. #if defined(DETHRACE_REPLAY_DEBUG)
  333.         ((tPipe_session*)gLocal_buffer)->pipe_magic1 = REPLAY_DEBUG_SESSION_MAGIC1;
  334. #endif
  335.         gLocal_buffer_size = offsetof(tPipe_session, chunks);
  336.         gMr_chunky = &((tPipe_session*)gLocal_buffer)->chunks;
  337.     }
  338. }
  339.  
  340. // IDA: void __usercall StartPipingSession(tPipe_chunk_type pThe_type@<EAX>)
  341. void StartPipingSession(tPipe_chunk_type pThe_type) {
  342.     LOG_TRACE("(%d)", pThe_type);
  343.  
  344.     StartPipingSession2(pThe_type, 1);
  345. }
  346.  
  347. // IDA: void __usercall EndPipingSession2(int pMunge_reentrancy@<EAX>)
  348. void EndPipingSession2(int pMunge_reentrancy) {
  349.     int a;
  350.     LOG_TRACE("(%d)", pMunge_reentrancy);
  351.  
  352.     if (gPipe_buffer_start != NULL && !gAction_replay_mode && gProgram_state.racing) {
  353.         // Each session ends with a tU16, containing the session size
  354.         *(tU16*)&gLocal_buffer[gLocal_buffer_size] = gLocal_buffer_size;
  355.         a = gLocal_buffer_size;
  356.         gLocal_buffer_size += sizeof(tU16);
  357.         REPLAY_DEBUG_ASSERT(LengthOfSession((tPipe_session*)gLocal_buffer) == gLocal_buffer_size);
  358. #if defined(DETHRACE_FIX_BUGS)
  359.         gLocal_buffer_size = PIPE_ALIGN(gLocal_buffer_size);
  360.         *(tU16*)&gLocal_buffer[gLocal_buffer_size - sizeof(tU16)] = gLocal_buffer_size - sizeof(tU16);
  361. #endif
  362.         if (((tPipe_session*)gLocal_buffer)->number_of_chunks != 0 && (gLocal_buffer_size < LOCAL_BUFFER_SIZE || a == LOCAL_BUFFER_SIZE - 2)) {
  363.             if (gPipe_buffer_phys_end < gPipe_record_ptr + gLocal_buffer_size) {
  364.                 // Put session at begin of pipe, as no place at end
  365.                 gPipe_buffer_working_end = gPipe_record_ptr;
  366.                 gPipe_buffer_oldest = gPipe_buffer_start;
  367.                 gPipe_record_ptr = gPipe_buffer_start;
  368.             }
  369.             while (gPipe_record_ptr <= gPipe_buffer_oldest && gPipe_buffer_oldest < gPipe_record_ptr + gLocal_buffer_size) {
  370.                 // Remove older sessions
  371. #if defined(DETHRACE_FIX_BUGS)
  372.                 gPipe_buffer_oldest += PIPE_ALIGN(LengthOfSession((tPipe_session*)gPipe_buffer_oldest));
  373. #else
  374.                 gPipe_buffer_oldest += LengthOfSession((tPipe_session*)gPipe_buffer_oldest);
  375. #endif
  376.                 if (gPipe_buffer_working_end <= gPipe_buffer_oldest) {
  377.                     gPipe_buffer_working_end = gPipe_buffer_phys_end;
  378.                     gPipe_buffer_oldest = gPipe_buffer_start;
  379.                 }
  380.             }
  381.             if (gPipe_buffer_oldest == NULL) {
  382.                 gPipe_buffer_oldest = gPipe_record_ptr;
  383.             }
  384.             memcpy(gPipe_record_ptr, gLocal_buffer, gLocal_buffer_size);
  385.             gPipe_record_ptr += gLocal_buffer_size;
  386.             if (gPipe_buffer_working_end < gPipe_record_ptr) {
  387.                 gPipe_buffer_working_end = gPipe_record_ptr;
  388.             }
  389.         }
  390.         if (pMunge_reentrancy) {
  391.             if (gReentrancy_count != 0) {
  392.                 gReentrancy_count--;
  393.                 if (gReentrancy_count != 0) {
  394.                     StartPipingSession2(gReentrancy_array[gReentrancy_count - 1], 0);
  395.                 }
  396.             }
  397.         }
  398.     }
  399. }
  400.  
  401. // IDA: void __cdecl EndPipingSession()
  402. void EndPipingSession(void) {
  403.     LOG_TRACE("()");
  404.  
  405.     EndPipingSession2(1);
  406. }
  407.  
  408. // IDA: void __usercall AddDataToSession(int pSubject_index@<EAX>, void *pData@<EDX>, tU32 pData_length@<EBX>)
  409. void AddDataToSession(int pSubject_index, void* pData, tU32 pData_length) {
  410.     tU32 temp_buffer_size;
  411.     //int variable_for_breaking_on; // Pierre-Marie Baty -- unused variable
  412.     LOG_TRACE("(%d, %p, %d)", pSubject_index, pData, pData_length);
  413.  
  414.     if (gPipe_buffer_start != NULL && !gAction_replay_mode && gProgram_state.racing) {
  415.         temp_buffer_size = gLocal_buffer_size + offsetof(tPipe_chunk, chunk_data) + pData_length;
  416.         if (temp_buffer_size >= LOCAL_BUFFER_SIZE) {
  417.             return;
  418.         }
  419.         REPLAY_DEBUG_ASSERT(((tPipe_session*)gLocal_buffer)->pipe_magic1 == REPLAY_DEBUG_SESSION_MAGIC1);
  420.         ((tPipe_session*)gLocal_buffer)->number_of_chunks++;
  421.         gMr_chunky->subject_index = pSubject_index;
  422. #if defined(DETHRACE_REPLAY_DEBUG)
  423.         gMr_chunky->chunk_magic1 = REPLAY_DEBUG_CHUNK_MAGIC1;
  424. #endif
  425.         memcpy(&gMr_chunky->chunk_data, pData, pData_length);
  426.         gMr_chunky = (tPipe_chunk*)(((tU8*)&gMr_chunky->chunk_data) + pData_length);
  427.         gLocal_buffer_size = temp_buffer_size;
  428.     }
  429. }
  430.  
  431. // IDA: void __usercall AddModelGeometryToPipingSession(tU16 pCar_ID@<EAX>, int pModel_index@<EDX>, int pVertex_count@<EBX>, tChanged_vertex *pCoordinates@<ECX>)
  432. void AddModelGeometryToPipingSession(tU16 pCar_ID, int pModel_index, int pVertex_count, tChanged_vertex* pCoordinates) {
  433.     tU32 data_size;
  434.     LOG_TRACE("(%d, %d, %d, %p)", pCar_ID, pModel_index, pVertex_count, pCoordinates);
  435.  
  436.     if (gModel_geometry_space != NULL) {
  437.         if (pVertex_count > 600) {
  438.             pVertex_count = 600;
  439.         }
  440.         data_size = offsetof(tPipe_model_geometry_data, vertex_changes) + pVertex_count * sizeof(tChanged_vertex);
  441.         gModel_geometry_space->vertex_count = pVertex_count;
  442.         gModel_geometry_space->model_index = pModel_index;
  443.         memcpy(gModel_geometry_space->vertex_changes, pCoordinates, pVertex_count * sizeof(tChanged_vertex));
  444.         AddDataToSession(pCar_ID, gModel_geometry_space, data_size);
  445.     }
  446. }
  447.  
  448. // IDA: void __usercall AddSmudgeToPipingSession(tU16 pCar_ID@<EAX>, int pModel_index@<EDX>, int pVertex_count@<EBX>, tSmudged_vertex *pCoordinates@<ECX>)
  449. void AddSmudgeToPipingSession(tU16 pCar_ID, int pModel_index, int pVertex_count, tSmudged_vertex* pCoordinates) {
  450.     tU32 data_size;
  451.     LOG_TRACE("(%d, %d, %d, %p)", pCar_ID, pModel_index, pVertex_count, pCoordinates);
  452.  
  453.     if (gSmudge_space != NULL) {
  454.         if (pVertex_count > 600) {
  455.             pVertex_count = 600;
  456.         }
  457.         gSmudge_space->vertex_count = pVertex_count;
  458.         gSmudge_space->model_index = pModel_index;
  459.         memcpy(gSmudge_space->vertex_changes, pCoordinates, pVertex_count * sizeof(tSmudged_vertex));
  460.         data_size = offsetof(tPipe_smudge_data, vertex_changes) + pVertex_count * sizeof(tSmudged_vertex);
  461.         AddDataToSession(pCar_ID, gSmudge_space, data_size);
  462.     }
  463. }
  464.  
  465. // IDA: void __usercall AddPedestrianToPipingSession(int pPedestrian_index@<EAX>, br_matrix34 *pTrans@<EDX>, tU8 pAction_index@<EBX>, tU8 pFrame_index@<ECX>, tS8 pHit_points, int pDone_initial, tU16 pParent_ID, float pSpin_period, br_scalar pJump_magnitude, br_vector3 *pOffset)
  466. void AddPedestrianToPipingSession(int pPedestrian_index, br_matrix34* pTrans, tU8 pAction_index, tU8 pFrame_index, tS8 pHit_points, int pDone_initial, tU16 pParent_ID, float pSpin_period, br_scalar pJump_magnitude, br_vector3* pOffset) {
  467.     tPipe_pedestrian_data data;
  468.     tU32 data_size;
  469.     LOG_TRACE("(%d, %p, %d, %d, %d, %d, %d, %f, %f, %p)", pPedestrian_index, pTrans, pAction_index, pFrame_index, pHit_points, pDone_initial, pParent_ID, pSpin_period, pJump_magnitude, pOffset);
  470.  
  471.     if (pFrame_index == 0xff) {
  472.         pFrame_index = 0;
  473.     }
  474.     data.action_and_frame_index = (pDone_initial ? 1 : 0) << 7 | pAction_index << 4 | pFrame_index;
  475.     data.hit_points = pHit_points;
  476.     data.new_translation.v[0] = pTrans->m[3][0];
  477.     data.new_translation.v[1] = pTrans->m[3][1];
  478.     data.new_translation.v[2] = pTrans->m[3][2];
  479.     data.parent = pParent_ID;
  480.     if (pHit_points <= 0) {
  481.         data.spin_period = pSpin_period;
  482.         data.parent_actor = GetPedestrianActor(pPedestrian_index)->parent;
  483.         BrVector3Copy(&data.offset, pOffset);
  484.         data.jump_magnitude = pJump_magnitude;
  485.         data_size = sizeof(tPipe_pedestrian_data);
  486.     } else {
  487.         data_size = offsetof(tPipe_pedestrian_data, spin_period);
  488.     }
  489.     AddDataToSession(pPedestrian_index, &data, data_size);
  490. }
  491.  
  492. // IDA: void __usercall AddSparkToPipingSession(int pSpark_index@<EAX>, br_vector3 *pPos@<EDX>, br_vector3 *pV@<EBX>)
  493. void AddSparkToPipingSession(int pSpark_index, br_vector3* pPos, br_vector3* pV) {
  494.     tPipe_spark_data data;
  495.     LOG_TRACE("(%d, %p, %p)", pSpark_index, pPos, pV);
  496.  
  497.     BrVector3Copy(&data.pos, pPos);
  498.     BrVector3Copy(&data.v, pV);
  499.     AddDataToSession(pSpark_index, &data, sizeof(tPipe_spark_data));
  500. }
  501.  
  502. // IDA: void __usercall AddShrapnelToPipingSession(int pShrapnel_index@<EAX>, br_vector3 *pPos@<EDX>, tU16 pAge@<EBX>, br_material *pMaterial@<ECX>)
  503. void AddShrapnelToPipingSession(int pShrapnel_index, br_vector3* pPos, tU16 pAge, br_material* pMaterial) {
  504.     tPipe_shrapnel_data data;
  505.     tU32 data_size;
  506.     LOG_TRACE("(%d, %p, %d, %p)", pShrapnel_index, pPos, pAge, pMaterial);
  507.  
  508.     BrVector3Copy(&data.pos, pPos);
  509.     if ((pShrapnel_index & 0x8000) != 0) {
  510.         data.age = pAge;
  511.         data.material = pMaterial;
  512.         data_size = sizeof(tPipe_shrapnel_data);
  513.     } else {
  514.         data_size = offsetof(tPipe_shrapnel_data, age);
  515.     }
  516.     AddDataToSession(pShrapnel_index, &data, data_size);
  517. }
  518.  
  519. // IDA: void __usercall AddScreenWobbleToPipingSession(int pWobble_x@<EAX>, int pWobble_y@<EDX>)
  520. void AddScreenWobbleToPipingSession(int pWobble_x, int pWobble_y) {
  521.     tPipe_screen_shake_data data;
  522.     LOG_TRACE("(%d, %d)", pWobble_x, pWobble_y);
  523.  
  524.     data.wobble_x = pWobble_x;
  525.     data.wobble_y = pWobble_y;
  526.     AddDataToSession(0, &data, sizeof(tPipe_screen_shake_data));
  527. }
  528.  
  529. // IDA: void __usercall AddGrooveStopToPipingSession(int pGroove_index@<EAX>, br_matrix34 *pMatrix@<EDX>, int pPath_interrupt@<EBX>, int pObject_interrupt@<ECX>, float pPath_resumption, float pObject_resumption)
  530. void AddGrooveStopToPipingSession(int pGroove_index, br_matrix34* pMatrix, int pPath_interrupt, int pObject_interrupt, float pPath_resumption, float pObject_resumption) {
  531.     tPipe_groove_stop_data data;
  532.     LOG_TRACE("(%d, %p, %d, %d, %f, %f)", pGroove_index, pMatrix, pPath_interrupt, pObject_interrupt, pPath_resumption, pObject_resumption);
  533.  
  534.     BrMatrix34Copy(&data.matrix, pMatrix);
  535.     data.path_interrupt = pPath_interrupt;
  536.     data.object_interrupt = pObject_interrupt;
  537.     data.path_resumption = pPath_resumption;
  538.     data.object_resumption = pObject_resumption;
  539.     AddDataToSession(pGroove_index, &data, sizeof(tPipe_groove_stop_data));
  540. }
  541.  
  542. // IDA: void __usercall AddNonCarToPipingSession(int pIndex@<EAX>, br_actor *pActor@<EDX>)
  543. void AddNonCarToPipingSession(int pIndex, br_actor* pActor) {
  544.     tPipe_non_car_data data;
  545.     LOG_TRACE("(%d, %p)", pIndex, pActor);
  546.  
  547.     BrMatrix34Copy(&data.mat, &pActor->t.t.mat);
  548.     data.actor = pActor;
  549.     AddDataToSession(pIndex, &data, sizeof(tPipe_non_car_data));
  550. }
  551.  
  552. // IDA: void __usercall AddSmokeToPipingSession(int pIndex@<EAX>, tU8 pType@<EDX>, br_vector3 *pPos@<EBX>, br_scalar pRadius, br_scalar pStrength)
  553. void AddSmokeToPipingSession(int pIndex, tU8 pType, br_vector3* pPos, br_scalar pRadius, br_scalar pStrength) {
  554.     tPipe_smoke_data data;
  555.     LOG_TRACE("(%d, %d, %p, %f, %f)", pIndex, pType, pPos, pRadius, pStrength);
  556.  
  557.     SaveReducedPos(&data.pos, pPos);
  558.     data.type = pType;
  559.     data.radius = pRadius * 1024.f;
  560.     data.strength = pStrength * 255.f;
  561.     AddDataToSession(pIndex, &data, sizeof(tPipe_smoke_data));
  562. }
  563.  
  564. // IDA: void __usercall AddSmokeColumnToPipingSession(int pIndex@<EAX>, tCar_spec *pCar@<EDX>, int pVertex@<EBX>, int pColour@<ECX>)
  565. void AddSmokeColumnToPipingSession(int pIndex, tCar_spec* pCar, int pVertex, int pColour) {
  566.     tPipe_smoke_column_data data;
  567.     LOG_TRACE("(%d, %p, %d, %d)", pIndex, pCar, pVertex, pColour);
  568.  
  569.     data.car_ID = pCar->car_ID;
  570.     data.vertex = pVertex;
  571.     AddDataToSession(pColour << 14 | pIndex, &data, sizeof(tPipe_smoke_column_data));
  572. }
  573.  
  574. // IDA: void __usercall AddFlameToPipingSession(int pIndex@<EAX>, int pFrame_count@<EDX>, br_scalar pScale_x, br_scalar pScale_y, br_scalar pOffset_x, br_scalar pOffset_z)
  575. void AddFlameToPipingSession(int pIndex, int pFrame_count, br_scalar pScale_x, br_scalar pScale_y, br_scalar pOffset_x, br_scalar pOffset_z) {
  576.     tPipe_flame_data data;
  577.     LOG_TRACE("(%d, %d, %f, %f, %f, %f)", pIndex, pFrame_count, pScale_x, pScale_y, pOffset_x, pOffset_z);
  578.  
  579.     data.frame_count = pFrame_count;
  580.     data.scale_x = pScale_x;
  581.     data.scale_y = pScale_y;
  582.     data.offset_x = pOffset_x;
  583.     data.offset_z = pOffset_z;
  584.     AddDataToSession(pIndex, &data, sizeof(tPipe_flame_data));
  585. }
  586.  
  587. // IDA: void __usercall AddSplashToPipingSession(tCollision_info *pCar@<EAX>)
  588. void AddSplashToPipingSession(tCollision_info* pCar) {
  589.     tPipe_splash_data data;
  590.     LOG_TRACE("(%p)", pCar);
  591.  
  592.     if (pCar->driver >= eDriver_oppo) {
  593.         data.d = pCar->water_d;
  594.         BrVector3Copy(&data.normal, &pCar->water_normal);
  595.         AddDataToSession(pCar->car_ID, &data, sizeof(tPipe_splash_data));
  596.     }
  597. }
  598.  
  599. // IDA: void __usercall AddOilSpillToPipingSession(int pIndex@<EAX>, br_matrix34 *pMat@<EDX>, br_scalar pFull_size, br_scalar pGrow_rate, tU32 pSpill_time, tU32 pStop_time, tCar_spec *pCar, br_vector3 *pOriginal_pos, br_pixelmap *pPixelmap)
  600. void AddOilSpillToPipingSession(int pIndex, br_matrix34* pMat, br_scalar pFull_size, br_scalar pGrow_rate, tU32 pSpill_time, tU32 pStop_time, tCar_spec* pCar, br_vector3* pOriginal_pos, br_pixelmap* pPixelmap) {
  601.     tPipe_oil_spill_data data;
  602.     LOG_TRACE("(%d, %p, %f, %f, %d, %d, %p, %p, %p)", pIndex, pMat, pFull_size, pGrow_rate, pSpill_time, pStop_time, pCar, pOriginal_pos, pPixelmap);
  603.  
  604.     BrMatrix34Copy(&data.mat, pMat);
  605.     data.full_size = pFull_size;
  606.     data.grow_rate = pGrow_rate;
  607.     data.spill_time = pSpill_time;
  608.     data.previous_stop_time = pStop_time;
  609.     data.car = pCar;
  610.     BrVector3Copy(&data.original_pos, pOriginal_pos);
  611.     data.pixelmap = pPixelmap;
  612.     AddDataToSession(pIndex, &data, sizeof(tPipe_oil_spill_data));
  613. }
  614.  
  615. // IDA: void __usercall AddFrameFinishToPipingSession(tU32 pThe_time@<EAX>)
  616. void AddFrameFinishToPipingSession(tU32 pThe_time) {
  617.     tPipe_frame_boundary_data data;
  618.     LOG_TRACE("(%d)", pThe_time);
  619.  
  620.     data.time = pThe_time;
  621.     AddDataToSession(0, &data, sizeof(tPipe_frame_boundary_data));
  622. }
  623.  
  624. // IDA: void __usercall AddCarToPipingSession(int pCar_ID@<EAX>, br_matrix34 *pCar_mat@<EDX>, br_vector3 *pCar_velocity@<EBX>, float pSpeedo_speed, float pLf_sus_position, float pRf_sus_position, float pLr_sus_position, float pRr_sus_position, float pSteering_angle, br_scalar pRevs, int pGear, int pFrame_coll_flag)
  625. void AddCarToPipingSession(int pCar_ID, br_matrix34* pCar_mat, br_vector3* pCar_velocity, float pSpeedo_speed, float pLf_sus_position, float pRf_sus_position, float pLr_sus_position, float pRr_sus_position, float pSteering_angle, br_scalar pRevs, int pGear, int pFrame_coll_flag) {
  626.     tPipe_car_data data;
  627.     LOG_TRACE("(%d, %p, %p, %f, %f, %f, %f, %f, %f, %f, %d, %d)", pCar_ID, pCar_mat, pCar_velocity, pSpeedo_speed, pLf_sus_position, pRf_sus_position, pLr_sus_position, pRr_sus_position, pSteering_angle, pRevs, pGear, pFrame_coll_flag);
  628.  
  629.     BrMatrix34Copy(&data.transformation, pCar_mat);
  630.     BrVector3Copy(&data.velocity, pCar_velocity);
  631.     data.speedo_speed = pSpeedo_speed * 32767.f / 0.07f;
  632.     data.lf_sus_position = pLf_sus_position * 127.f / .15f;
  633.     data.rf_sus_position = pRf_sus_position * 127.f / .15f;
  634.     data.lr_sus_position = pLr_sus_position * 127.f / .15f;
  635.     data.rr_sus_position = pRr_sus_position * 127.f / .15f;
  636.     data.steering_angle = pSteering_angle * 32767.f / 60.f;
  637.     data.revs_and_gear = (pGear + 1) << 12 | (pFrame_coll_flag ? 0 : 1) << 11 | ((((int)pRevs) / 10) & 0x7ff);
  638.     AddDataToSession(pCar_ID, &data, sizeof(tPipe_car_data));
  639. }
  640.  
  641. // IDA: void __usercall AddSoundToPipingSession(tS3_outlet_ptr pOutlet@<EAX>, int pSound_index@<EDX>, tS3_volume pL_volume@<EBX>, tS3_volume pR_volume@<ECX>, tS3_pitch pPitch, br_vector3 *pPos)
  642. void AddSoundToPipingSession(tS3_outlet_ptr pOutlet, int pSound_index, tS3_volume pL_volume, tS3_volume pR_volume, tS3_pitch pPitch, br_vector3* pPos) {
  643.     tPipe_sound_data data;
  644.     LOG_TRACE("(%d, %d, %d, %d, %d, %p)", pOutlet, pSound_index, pL_volume, pR_volume, pPitch, pPos);
  645.  
  646.     data.pitch = pPitch;
  647.     if (pPos == NULL) {
  648.         BrVector3Set(&data.position, 0.f, 0.f, 0.f);
  649.     } else {
  650.         BrVector3Copy(&data.position, pPos);
  651.     }
  652.     data.volume = (pR_volume << 8) | (pL_volume << 0);
  653.     data.outlet_index = GetIndexFromOutlet(pOutlet);
  654.     AddDataToSession(pSound_index, &data, sizeof(tPipe_sound_data));
  655. }
  656.  
  657. // IDA: void __usercall AddDamageToPipingSession(int pCar_ID@<EAX>, tS8 *pDifferences@<EDX>)
  658. void AddDamageToPipingSession(int pCar_ID, tS8* pDifferences) {
  659.     tPipe_damage_data data;
  660.     int i;
  661.     LOG_TRACE("(%d, %p)", pCar_ID, pDifferences);
  662.  
  663.     for (i = 0; i < COUNT_OF(data.damage_delta); i++) {
  664.         data.damage_delta[i] = pDifferences[i];
  665.     }
  666.     AddDataToSession(pCar_ID, &data, sizeof(tPipe_damage_data));
  667. }
  668.  
  669. // IDA: void __usercall AddSpecialToPipingSession(tSpecial_type pType@<EAX>)
  670. void AddSpecialToPipingSession(tSpecial_type pType) {
  671.     tPipe_special_data data;
  672.     LOG_TRACE("(%d)", pType);
  673.  
  674.     AddDataToSession(pType, &data, sizeof(tPipe_special_data));
  675. }
  676.  
  677. // IDA: void __usercall AddPedGibToPipingSession(int pIndex@<EAX>, br_matrix34 *pTrans@<EDX>, int pSize@<EBX>, int pGib_index@<ECX>, int pPed_index)
  678. void AddPedGibToPipingSession(int pIndex, br_matrix34* pTrans, int pSize, int pGib_index, int pPed_index) {
  679.     tPipe_ped_gib_data data;
  680.     LOG_TRACE("(%d, %p, %d, %d, %d)", pIndex, pTrans, pSize, pGib_index, pPed_index);
  681.  
  682.     data.ped_parent_index = pPed_index;
  683.     data.size = pSize;
  684.     data.gib_index = pGib_index;
  685.     BrMatrix34Copy(&data.transform, pTrans);
  686.     AddDataToSession(pIndex, &data, sizeof(tPipe_ped_gib_data));
  687. }
  688.  
  689. // IDA: void __cdecl AddCarIncidentToPipingSession(float pSeverity, tCar_spec *pCar, br_vector3 *pImpact_point)
  690. void AddCarIncidentToPipingSession(float pSeverity, tCar_spec* pCar, br_vector3* pImpact_point) {
  691.     tPipe_incident_data data;
  692.     LOG_TRACE("(%f, %p, %p)", pSeverity, pCar, pImpact_point);
  693.  
  694.     data.severity = pSeverity;
  695.     data.info.car_info.car_ID = pCar->car_ID;
  696.     BrVector3Copy(&data.info.car_info.impact_point, pImpact_point);
  697.     AddDataToSession(1, &data, sizeof(tPipe_incident_data));
  698. }
  699.  
  700. // IDA: void __usercall AddPedIncidentToPipingSession(int pPed_index@<EAX>, br_actor *pActor@<EDX>)
  701. void AddPedIncidentToPipingSession(int pPed_index, br_actor* pActor) {
  702.     tPipe_incident_data data;
  703.     LOG_TRACE("(%d, %p)", pPed_index, pActor);
  704.  
  705.     data.severity = 0.f;
  706.     data.info.ped_info.ped_index = pPed_index;
  707.     data.info.ped_info.actor = pActor;
  708.     AddDataToSession(0, &data, sizeof(tPipe_incident_data));
  709. }
  710.  
  711. // IDA: void __cdecl AddWallIncidentToPipingSession(float pSeverity, br_vector3 *pImpact_point)
  712. void AddWallIncidentToPipingSession(float pSeverity, br_vector3* pImpact_point) {
  713.     tPipe_incident_data data;
  714.     LOG_TRACE("(%f, %p)", pSeverity, pImpact_point);
  715.  
  716.     data.severity = pSeverity;
  717.     BrVector3Copy(&data.info.wall_info.pos, pImpact_point);
  718.     AddDataToSession(2, &data, sizeof(tPipe_incident_data));
  719. }
  720.  
  721. // IDA: void __usercall AddProxRayToPipingSession(int pRay_index@<EAX>, tCar_spec *pCar@<EDX>, tU16 pPed_index@<EBX>, tU32 pTime@<ECX>)
  722. void AddProxRayToPipingSession(int pRay_index, tCar_spec* pCar, tU16 pPed_index, tU32 pTime) {
  723.     tPipe_prox_ray_data data;
  724.     LOG_TRACE("(%d, %p, %d, %d)", pRay_index, pCar, pPed_index, pTime);
  725.  
  726.     data.ped_index = pPed_index;
  727.     data.car_ID = pCar->car_ID;
  728.     data.time = pTime;
  729.     AddDataToSession(pRay_index, &data, sizeof(tPipe_prox_ray_data));
  730. }
  731.  
  732. // IDA: void __usercall AddSkidAdjustmentToPipingSession(int pSkid_num@<EAX>, br_matrix34 *pMatrix@<EDX>, int pMaterial_index@<EBX>)
  733. void AddSkidAdjustmentToPipingSession(int pSkid_num, br_matrix34* pMatrix, int pMaterial_index) {
  734.     tPipe_skid_adjustment adjustment;
  735.     LOG_TRACE("(%d, %p, %d)", pSkid_num, pMatrix, pMaterial_index);
  736.  
  737.     BrMatrix34Copy(&adjustment.matrix, pMatrix);
  738.     adjustment.material_index = pMaterial_index;
  739.     AddDataToSession(pSkid_num, &adjustment, sizeof(tPipe_skid_adjustment));
  740. }
  741.  
  742. // IDA: void __usercall PipeSingleModelGeometry(tU16 pCar_ID@<EAX>, int pModel_index@<EDX>, int pVertex_count@<EBX>, tChanged_vertex *pCoordinates@<ECX>)
  743. void PipeSingleModelGeometry(tU16 pCar_ID, int pModel_index, int pVertex_count, tChanged_vertex* pCoordinates) {
  744.     LOG_TRACE("(%d, %d, %d, %p)", pCar_ID, pModel_index, pVertex_count, pCoordinates);
  745.  
  746.     StartPipingSession(ePipe_chunk_model_geometry);
  747.     AddModelGeometryToPipingSession(pCar_ID, pModel_index, pVertex_count, pCoordinates);
  748.     EndPipingSession();
  749. }
  750.  
  751. // IDA: void __usercall PipeSinglePedestrian(int pPedestrian_index@<EAX>, br_matrix34 *pTrans@<EDX>, tU8 pAction_index@<EBX>, tU8 pFrame_index@<ECX>, tS8 pHit_points, int pDone_initial, tU16 pParent_ID, float pSpin_period, br_scalar pJump_magnitude, br_vector3 *pOffset)
  752. void PipeSinglePedestrian(int pPedestrian_index, br_matrix34* pTrans, tU8 pAction_index, tU8 pFrame_index, tS8 pHit_points, int pDone_initial, tU16 pParent_ID, float pSpin_period, br_scalar pJump_magnitude, br_vector3* pOffset) {
  753.     LOG_TRACE("(%d, %p, %d, %d, %d, %d, %d, %f, %f, %p)", pPedestrian_index, pTrans, pAction_index, pFrame_index, pHit_points, pDone_initial, pParent_ID, pSpin_period, pJump_magnitude, pOffset);
  754.  
  755.     StartPipingSession(ePipe_chunk_pedestrian);
  756.     AddPedestrianToPipingSession(pPedestrian_index, pTrans, pAction_index,
  757.         pFrame_index, pHit_points, pDone_initial, pParent_ID, pSpin_period,
  758.         pJump_magnitude, pOffset);
  759.     EndPipingSession();
  760. }
  761.  
  762. // IDA: void __usercall PipeSingleCar(int pCar_ID@<EAX>, br_matrix34 *pCar_mat@<EDX>, br_vector3 *pCar_velocity@<EBX>, float pSpeedo_speed, float pLf_sus_position, float pRf_sus_position, float pLr_sus_position, float pRr_sus_position, float pSteering_angle, br_scalar pRevs, int pGear, int pFrame_coll_flag)
  763. void PipeSingleCar(int pCar_ID, br_matrix34* pCar_mat, br_vector3* pCar_velocity, float pSpeedo_speed, float pLf_sus_position, float pRf_sus_position, float pLr_sus_position, float pRr_sus_position, float pSteering_angle, br_scalar pRevs, int pGear, int pFrame_coll_flag) {
  764.     LOG_TRACE("(%d, %p, %p, %f, %f, %f, %f, %f, %f, %f, %d, %d)", pCar_ID, pCar_mat, pCar_velocity, pSpeedo_speed, pLf_sus_position, pRf_sus_position, pLr_sus_position, pRr_sus_position, pSteering_angle, pRevs, pGear, pFrame_coll_flag);
  765.  
  766.     StartPipingSession(ePipe_chunk_car);
  767.     AddCarToPipingSession(pCar_ID, pCar_mat, pCar_velocity, pSpeedo_speed,
  768.         pLf_sus_position, pRf_sus_position, pLr_sus_position, pRr_sus_position,
  769.         pSteering_angle, pRevs, pGear, pFrame_coll_flag);
  770.     EndPipingSession();
  771. }
  772.  
  773. // IDA: void __usercall PipeSingleSound(tS3_outlet_ptr pOutlet@<EAX>, int pSound_index@<EDX>, tS3_volume pL_volume@<EBX>, tS3_volume pR_volume@<ECX>, tS3_pitch pPitch, br_vector3 *pPos)
  774. void PipeSingleSound(tS3_outlet_ptr pOutlet, int pSound_index, tS3_volume pL_volume, tS3_volume pR_volume, tS3_pitch pPitch, br_vector3* pPos) {
  775.     LOG_TRACE("(%d, %d, %d, %d, %d, %p)", pOutlet, pSound_index, pL_volume, pR_volume, pPitch, pPos);
  776.  
  777.     if (!gAction_replay_mode && gProgram_state.racing) {
  778.         StartPipingSession(ePipe_chunk_sound);
  779.         AddSoundToPipingSession(pOutlet, pSound_index, pL_volume, pR_volume, pPitch, pPos);
  780.         EndPipingSession();
  781.     }
  782. }
  783.  
  784. // IDA: void __usercall PipeSingleOilSpill(int pIndex@<EAX>, br_matrix34 *pMat@<EDX>, br_scalar pFull_size, br_scalar pGrow_rate, tU32 pSpill_time, tU32 pStop_time, tCar_spec *pCar, br_vector3 *pOriginal_pos, br_pixelmap *pPixelmap)
  785. void PipeSingleOilSpill(int pIndex, br_matrix34* pMat, br_scalar pFull_size, br_scalar pGrow_rate, tU32 pSpill_time, tU32 pStop_time, tCar_spec* pCar, br_vector3* pOriginal_pos, br_pixelmap* pPixelmap) {
  786.     LOG_TRACE("(%d, %p, %f, %f, %d, %d, %p, %p, %p)", pIndex, pMat, pFull_size, pGrow_rate, pSpill_time, pStop_time, pCar, pOriginal_pos, pPixelmap);
  787.  
  788.     StartPipingSession(ePipe_chunk_oil_spill);
  789.     AddOilSpillToPipingSession(pIndex, pMat, pFull_size, pGrow_rate,
  790.         pSpill_time, pStop_time, pCar, pOriginal_pos, pPixelmap);
  791.     EndPipingSession();
  792. }
  793.  
  794. // IDA: void __usercall PipeSingleDamage(int pCar_ID@<EAX>, tS8 *pDifferences@<EDX>)
  795. void PipeSingleDamage(int pCar_ID, tS8* pDifferences) {
  796.     LOG_TRACE("(%d, %p)", pCar_ID, pDifferences);
  797.  
  798.     StartPipingSession(ePipe_chunk_damage);
  799.     AddDamageToPipingSession(pCar_ID, pDifferences);
  800.     EndPipingSession();
  801. }
  802.  
  803. // IDA: void __usercall PipeSingleSpecial(tSpecial_type pType@<EAX>)
  804. void PipeSingleSpecial(tSpecial_type pType) {
  805.     LOG_TRACE("(%d)", pType);
  806.  
  807.     StartPipingSession(ePipe_chunk_special);
  808.     AddSpecialToPipingSession(pType);
  809.     EndPipingSession();
  810. }
  811.  
  812. // IDA: void __usercall PipeSinglePedGib(int pIndex@<EAX>, br_matrix34 *pTrans@<EDX>, int pSize@<EBX>, int pGib_index@<ECX>, int pPed_index)
  813. void PipeSinglePedGib(int pIndex, br_matrix34* pTrans, int pSize, int pGib_index, int pPed_index) {
  814.     LOG_TRACE("(%d, %p, %d, %d, %d)", pIndex, pTrans, pSize, pGib_index, pPed_index);
  815.  
  816.     StartPipingSession(ePipe_chunk_ped_gib);
  817.     AddPedGibToPipingSession(pIndex, pTrans, pSize, pGib_index, pPed_index);
  818.     EndPipingSession();
  819. }
  820.  
  821. // IDA: void __cdecl PipeSingleCarIncident(float pSeverity, tCar_spec *pCar, br_vector3 *pImpact_point)
  822. void PipeSingleCarIncident(float pSeverity, tCar_spec* pCar, br_vector3* pImpact_point) {
  823.     LOG_TRACE("(%f, %p, %p)", pSeverity, pCar, pImpact_point);
  824.  
  825.     StartPipingSession(ePipe_chunk_incident);
  826.     AddCarIncidentToPipingSession(pSeverity, pCar, pImpact_point);
  827.     EndPipingSession();
  828. }
  829.  
  830. // IDA: void __usercall PipeSinglePedIncident(int pPed_index@<EAX>, br_actor *pActor@<EDX>)
  831. void PipeSinglePedIncident(int pPed_index, br_actor* pActor) {
  832.     LOG_TRACE("(%d, %p)", pPed_index, pActor);
  833.  
  834.     StartPipingSession(ePipe_chunk_incident);
  835.     AddPedIncidentToPipingSession(pPed_index, pActor);
  836.     EndPipingSession();
  837.     gWall_severity = 0.f;
  838. }
  839.  
  840. // IDA: void __cdecl PipeSingleWallIncident(float pSeverity, br_vector3 *pImpact_point)
  841. void PipeSingleWallIncident(float pSeverity, br_vector3* pImpact_point) {
  842.     LOG_TRACE("(%f, %p)", pSeverity, pImpact_point);
  843.  
  844.     if (pSeverity > gWall_severity) {
  845.         gWall_severity = pSeverity;
  846.         BrVector3Copy(&gWall_impact_point, pImpact_point);
  847.     }
  848. }
  849.  
  850. // IDA: void __usercall PipeSingleScreenShake(int pWobble_x@<EAX>, int pWobble_y@<EDX>)
  851. void PipeSingleScreenShake(int pWobble_x, int pWobble_y) {
  852.     LOG_TRACE("(%d, %d)", pWobble_x, pWobble_y);
  853.  
  854.     StartPipingSession(ePipe_chunk_screen_shake);
  855.     AddScreenWobbleToPipingSession(pWobble_x, pWobble_y);
  856.     EndPipingSession();
  857. }
  858.  
  859. // IDA: void __usercall PipeSingleGrooveStop(int pGroove_index@<EAX>, br_matrix34 *pMatrix@<EDX>, int pPath_interrupt@<EBX>, int pObject_interrupt@<ECX>, float pPath_resumption, float pObject_resumption)
  860. void PipeSingleGrooveStop(int pGroove_index, br_matrix34* pMatrix, int pPath_interrupt, int pObject_interrupt, float pPath_resumption, float pObject_resumption) {
  861.     LOG_TRACE("(%d, %p, %d, %d, %f, %f)", pGroove_index, pMatrix, pPath_interrupt, pObject_interrupt, pPath_resumption, pObject_resumption);
  862.  
  863.     StartPipingSession(ePipe_chunk_groove_stop);
  864.     AddGrooveStopToPipingSession(pGroove_index, pMatrix, pPath_interrupt,
  865.         pObject_interrupt, pPath_resumption, pObject_resumption);
  866.     EndPipingSession();
  867. }
  868.  
  869. // IDA: void __cdecl PipeFrameFinish()
  870. void PipeFrameFinish(void) {
  871.     LOG_TRACE("()");
  872.  
  873.     if (gWall_severity != 0.f) {
  874.         StartPipingSession(ePipe_chunk_incident);
  875.         AddWallIncidentToPipingSession(gWall_severity, &gWall_impact_point);
  876.         EndPipingSession();
  877.         gWall_severity = 0.f;
  878.     }
  879.     StartPipingSession(ePipe_chunk_frame_boundary);
  880.     AddFrameFinishToPipingSession(GetTotalTime());
  881.     EndPipingSession();
  882. }
  883.  
  884. // IDA: void __cdecl PipingFrameReset()
  885. void PipingFrameReset(void) {
  886.     int i;
  887.     LOG_TRACE("()");
  888.  
  889.     for (i = 0; i < COUNT_OF(gReset_procs); i++) {
  890.         if (gReset_procs[i] != NULL) {
  891.             gReset_procs[i]();
  892.         }
  893.     }
  894. }
  895.  
  896. // IDA: void __usercall PipeSingleSkidAdjustment(int pSkid_num@<EAX>, br_matrix34 *pMatrix@<EDX>, int pMaterial_index@<EBX>)
  897. void PipeSingleSkidAdjustment(int pSkid_num, br_matrix34* pMatrix, int pMaterial_index) {
  898.     LOG_TRACE("(%d, %p, %d)", pSkid_num, pMatrix, pMaterial_index);
  899.  
  900.     StartPipingSession(ePipe_chunk_skid_adjustment);
  901.     AddSkidAdjustmentToPipingSession(pSkid_num, pMatrix, pMaterial_index);
  902.     EndPipingSession();
  903. }
  904.  
  905. // IDA: void __cdecl ResetPiping()
  906. void ResetPiping(void) {
  907.     LOG_TRACE("()");
  908.  
  909.     gWall_severity = 0.f;
  910.     gPipe_buffer_oldest = NULL;
  911.     gPipe_record_ptr = gPipe_buffer_start;
  912.     gPipe_buffer_working_end = gPipe_buffer_phys_end;
  913.     gReentrancy_count = 0;
  914. }
  915.  
  916. // IDA: void __cdecl InitialisePiping()
  917. void InitialisePiping(void) {
  918.     LOG_TRACE("()");
  919.  
  920.     if (!gAusterity_mode && gNet_mode == eNet_mode_none) {
  921.         PDAllocateActionReplayBuffer((char**)&gPipe_buffer_start, &gPipe_buffer_size);
  922.         gPipe_buffer_phys_end = gPipe_buffer_start + gPipe_buffer_size;
  923.         gSmudge_space = BrMemAllocate(offsetof(tPipe_smudge_data, vertex_changes) + sizeof(tSmudged_vertex) * 2400, kMem_pipe_model_geometry);
  924.         // DAT_00532008 = 0;
  925.         BrVector3SetFloat(&gZero_vector, 0.f, 0.f, 0.f);
  926.         gModel_geometry_space = (tPipe_model_geometry_data*)gSmudge_space;
  927.         gLocal_buffer = BrMemAllocate(LOCAL_BUFFER_SIZE, kMem_pipe_model_geometry);
  928.     } else {
  929.         gPipe_buffer_start = NULL;
  930.         gLocal_buffer = NULL;
  931.         gModel_geometry_space = NULL;
  932.         gSmudge_space = NULL;
  933.     }
  934.     ResetPiping();
  935. }
  936.  
  937. // IDA: void __cdecl DisposePiping()
  938. void DisposePiping(void) {
  939.     LOG_TRACE("()");
  940.  
  941.     if (gPipe_buffer_start != NULL) {
  942.         PDDisposeActionReplayBuffer((char*)gPipe_buffer_start);
  943.     }
  944.     gPipe_buffer_start = NULL;
  945.     if (gModel_geometry_space != NULL) {
  946.         BrMemFree(gModel_geometry_space);
  947.         gModel_geometry_space = NULL;
  948.     }
  949.     if (gLocal_buffer != NULL) {
  950.         BrMemFree(gLocal_buffer);
  951.         gLocal_buffer = NULL;
  952.     }
  953. }
  954.  
  955. // IDA: void __cdecl InitLastDamageArrayEtc()
  956. void InitLastDamageArrayEtc(void) {
  957.     int i;
  958.     int j;
  959.     int cat;
  960.     int car_count;
  961.     tCar_spec* car;
  962.     LOG_TRACE("()");
  963.  
  964.     for (cat = eVehicle_self; cat <= eVehicle_not_really; cat++) {
  965.         if (cat == eVehicle_self) {
  966.             car_count = 1;
  967.         } else {
  968.             car_count = GetCarCount(cat);
  969.         }
  970.         for (i = 0; i < car_count; i++) {
  971.             if (cat == eVehicle_self) {
  972.                 car = &gProgram_state.current_car;
  973.             } else {
  974.                 car = GetCarSpec(cat, i);
  975.             }
  976.             if (cat != eVehicle_not_really) {
  977.                 for (j = 0; j < COUNT_OF(car->frame_start_damage); j++) {
  978.                     car->frame_start_damage[j] = 0;
  979.                 }
  980.             }
  981.             car->car_ID = (cat << 8) | i;
  982.         }
  983.     }
  984. }
  985.  
  986. // IDA: void __cdecl ResetCars()
  987. void ResetCars(void) {
  988.     tCar_spec* car;
  989.     int cat;
  990.     int i;
  991.     int car_count;
  992.     LOG_TRACE("()");
  993.  
  994.     for (cat = eVehicle_self; cat < eVehicle_not_really; cat++) {
  995.         if (cat == eVehicle_self) {
  996.             car_count = 1;
  997.         } else {
  998.             car_count = GetCarCount(cat);
  999.         }
  1000.         for (i = 0; i < car_count; i++) {
  1001.             if (cat == eVehicle_self) {
  1002.                 car = &gProgram_state.current_car;
  1003.             } else {
  1004.                 car = GetCarSpec(cat, i);
  1005.             }
  1006.             car->active = 0;
  1007.         }
  1008.     }
  1009. }
  1010.  
  1011. // IDA: void __cdecl PipeCarPositions()
  1012. void PipeCarPositions(void) {
  1013.     tCar_spec* car;
  1014.     int cat;
  1015.     int i;
  1016.     int j;
  1017.     int car_count;
  1018.     int session_started;
  1019.     int difference_found;
  1020.     tS8 damage_deltas[12];
  1021.     LOG_TRACE("()");
  1022.  
  1023.     StartPipingSession(ePipe_chunk_car);
  1024.     for (cat = eVehicle_self; cat < eVehicle_not_really; cat++) {
  1025.         if (cat == eVehicle_self) {
  1026.             car_count = 1;
  1027.         } else {
  1028.             car_count = GetCarCount(cat);
  1029.         }
  1030.         for (i = 0; i < car_count; i++) {
  1031.             if (cat == eVehicle_self) {
  1032.                 car = &gProgram_state.current_car;
  1033.             } else {
  1034.                 car = GetCarSpec(cat, i);
  1035.             }
  1036.             AddCarToPipingSession((cat << 8) | i,
  1037.                 &car->car_master_actor->t.t.mat, &car->v, car->speedo_speed,
  1038.                 car->lf_sus_position, car->rf_sus_position, car->lr_sus_position, car->rr_sus_position,
  1039.                 car->steering_angle, car->revs, car->gear, car->frame_collision_flag);
  1040.         }
  1041.     }
  1042.     EndPipingSession();
  1043.     session_started = 0;
  1044.     for (cat = eVehicle_self; cat < eVehicle_net_player; cat++) {
  1045.         if (cat == eVehicle_self) {
  1046.             car_count = 1;
  1047.         } else {
  1048.             car_count = GetCarCount(cat);
  1049.         }
  1050.         for (i = 0; i < car_count; i++) {
  1051.             if (cat == eVehicle_self) {
  1052.                 car = &gProgram_state.current_car;
  1053.             } else {
  1054.                 car = GetCarSpec(cat, i);
  1055.             }
  1056.             if (car->active) {
  1057.                 difference_found = 0;
  1058.                 for (j = 0; j < COUNT_OF(car->damage_units); j++) {
  1059.                     damage_deltas[j] = car->damage_units[j].damage_level - car->frame_start_damage[j];
  1060.                     difference_found |= damage_deltas[j];
  1061.                     car->frame_start_damage[j] = car->damage_units[j].damage_level;
  1062.                 }
  1063.                 if (difference_found) {
  1064.                     if (!session_started) {
  1065.                         StartPipingSession(ePipe_chunk_damage);
  1066.                         session_started = 1;
  1067.                     }
  1068.                     AddDamageToPipingSession((cat << 8) | i, damage_deltas);
  1069.                 }
  1070.             }
  1071.         }
  1072.     }
  1073.     if (session_started) {
  1074.         EndPipingSession();
  1075.     }
  1076. }
  1077.  
  1078. // IDA: void __cdecl ResetPipePlayToEnd()
  1079. void ResetPipePlayToEnd(void) {
  1080.     LOG_TRACE("()");
  1081.  
  1082.     gPipe_play_ptr = gPipe_record_ptr;
  1083. }
  1084.  
  1085. // IDA: void __cdecl ResetPipePlayToStart()
  1086. void ResetPipePlayToStart(void) {
  1087.     LOG_TRACE("()");
  1088.  
  1089.     gPipe_play_ptr = gPipe_buffer_oldest;
  1090. }
  1091.  
  1092. // IDA: tU8* __cdecl GetPipePlayPtr()
  1093. tU8* GetPipePlayPtr(void) {
  1094.     LOG_TRACE("()");
  1095.  
  1096.     return gPipe_play_ptr;
  1097. }
  1098.  
  1099. // IDA: void __usercall SetPipePlayPtr(tU8 *pPtr@<EAX>)
  1100. void SetPipePlayPtr(tU8* pPtr) {
  1101.     LOG_TRACE("(%p)", pPtr);
  1102.  
  1103.     gPipe_play_ptr = pPtr;
  1104. }
  1105.  
  1106. // IDA: void __usercall AdvanceChunkPtr(tPipe_chunk **pChunk@<EAX>, tChunk_subject_index pType@<EDX>)
  1107. void AdvanceChunkPtr(tPipe_chunk** pChunk, tChunk_subject_index pType) {
  1108.     tPipe_chunk* old_chunk;
  1109.     LOG_TRACE("(%p, %d)", pChunk, pType);
  1110.  
  1111.     old_chunk = *pChunk;
  1112.     if (gDisable_advance) {
  1113.         return;
  1114.     }
  1115.     switch (pType) {
  1116.     case ePipe_chunk_model_geometry:
  1117.         *(tU8**)pChunk += offsetof(tPipe_model_geometry_data, vertex_changes) + (*pChunk)->chunk_data.model_geometry_data.vertex_count * sizeof(tChanged_vertex);
  1118.         break;
  1119.     case ePipe_chunk_pedestrian:
  1120.         *(tU8**)pChunk += (((*pChunk)->chunk_data.pedestrian_data.hit_points <= 0) ? sizeof(tPipe_pedestrian_data) : offsetof(tPipe_pedestrian_data, spin_period));
  1121.         break;
  1122.     case ePipe_chunk_frame_boundary:
  1123.         *(tU8**)pChunk += sizeof(tPipe_frame_boundary_data);
  1124.         break;
  1125.     case ePipe_chunk_car:
  1126.         *(tU8**)pChunk += sizeof(tPipe_car_data);
  1127.         break;
  1128.     case ePipe_chunk_sound:
  1129.         *(tU8**)pChunk += sizeof(tPipe_sound_data);
  1130.         break;
  1131.     case ePipe_chunk_damage:
  1132.         *(tU8**)pChunk += sizeof(tPipe_damage_data);
  1133.         break;
  1134.     case ePipe_chunk_special:
  1135.         *(tU8**)pChunk += sizeof(tPipe_special_data);
  1136.         break;
  1137.     case ePipe_chunk_ped_gib:
  1138.         *(tU8**)pChunk += sizeof(tPipe_ped_gib_data);
  1139.         break;
  1140.     case ePipe_chunk_incident:
  1141.         *(tU8**)pChunk += sizeof(tPipe_incident_data);
  1142.         break;
  1143.     case ePipe_chunk_spark:
  1144.         *(tU8**)pChunk += sizeof(tPipe_spark_data);
  1145.         break;
  1146.     case ePipe_chunk_shrapnel:
  1147.         *(tU8**)pChunk += (((*pChunk)->subject_index & 0x8000) ? sizeof(tPipe_shrapnel_data) : offsetof(tPipe_shrapnel_data, age));
  1148.         break;
  1149.     case ePipe_chunk_screen_shake:
  1150.         *(tU8**)pChunk += sizeof(tPipe_screen_shake_data);
  1151.         break;
  1152.     case ePipe_chunk_groove_stop:
  1153.         *(tU8**)pChunk += sizeof(tPipe_groove_stop_data);
  1154.         break;
  1155.     case ePipe_chunk_non_car:
  1156.         *(tU8**)pChunk += sizeof(tPipe_non_car_data);
  1157.         break;
  1158.     case ePipe_chunk_smoke:
  1159.         *(tU8**)pChunk += sizeof(tPipe_smoke_data);
  1160.         break;
  1161.     case ePipe_chunk_oil_spill:
  1162.         *(tU8**)pChunk += sizeof(tPipe_oil_spill_data);
  1163.         break;
  1164.     case ePipe_chunk_smoke_column:
  1165.         *(tU8**)pChunk += sizeof(tPipe_smoke_column_data);
  1166.         break;
  1167.     case ePipe_chunk_flame:
  1168.         *(tU8**)pChunk += sizeof(tPipe_flame_data);
  1169.         break;
  1170.     case ePipe_chunk_smudge:
  1171.         *(tU8**)pChunk += offsetof(tPipe_smudge_data, vertex_changes) + (*pChunk)->chunk_data.smudge_data.vertex_count * sizeof(tSmudged_vertex);
  1172.         break;
  1173.     case ePipe_chunk_splash:
  1174.         *(tU8**)pChunk += sizeof(tPipe_splash_data);
  1175.         break;
  1176.     case ePipe_chunk_prox_ray:
  1177.         *(tU8**)pChunk += sizeof(tPipe_prox_ray_data);
  1178.         break;
  1179.     case ePipe_chunk_skid_adjustment:
  1180.         *(tU8**)pChunk += sizeof(tPipe_skid_adjustment);
  1181.         break;
  1182.     }
  1183.     *(tU8**)pChunk += offsetof(tPipe_chunk, chunk_data);
  1184.  
  1185.     /* Fail-safe to avoid reading junk data from the session after */
  1186.     if (*(tU8**)pChunk == gEnd_of_session) {
  1187.         *pChunk = old_chunk;
  1188.     } else if (*(tU8**)pChunk > gEnd_of_session) {
  1189.         *pChunk = old_chunk;
  1190.     }
  1191. }
  1192.  
  1193. // IDA: void __usercall ApplyModelGeometry(tPipe_chunk **pChunk@<EAX>)
  1194. void ApplyModelGeometry(tPipe_chunk** pChunk) {
  1195.     int i;
  1196.     br_model* model_ptr;
  1197.     tCar_spec* car;
  1198.     LOG_TRACE("(%p)", pChunk);
  1199.  
  1200.     if (((*pChunk)->subject_index & 0xff00) == 0) {
  1201.         car = &gProgram_state.current_car;
  1202.     } else {
  1203.         car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0xff);
  1204.     }
  1205.     model_ptr = car->car_model_actors[(*pChunk)->chunk_data.model_geometry_data.model_index].actor->model;
  1206.     for (i = 0; i < (*pChunk)->chunk_data.model_geometry_data.vertex_count; i++) {
  1207.         BrVector3Accumulate(&model_ptr->vertices[(*pChunk)->chunk_data.model_geometry_data.vertex_changes[i].vertex_index].p,
  1208.             &(*pChunk)->chunk_data.model_geometry_data.vertex_changes[i].delta_coordinates);
  1209.     }
  1210.     SetModelForUpdate(model_ptr, car, 0);
  1211.     AdvanceChunkPtr(pChunk, ePipe_chunk_model_geometry);
  1212. }
  1213.  
  1214. // IDA: void __usercall DoSmudge(tPipe_chunk **pChunk@<EAX>, int pDir@<EDX>)
  1215. void DoSmudge(tPipe_chunk** pChunk, int pDir) {
  1216.     int i;
  1217.     int v;
  1218.     tU8 inc;
  1219.     br_model* model_ptr;
  1220.     tCar_spec* car;
  1221.     //int group; // Pierre-Marie Baty -- unused variable
  1222.     LOG_TRACE("(%p, %d)", pChunk, pDir);
  1223.  
  1224.     if (((*pChunk)->subject_index & 0xff00) == 0) {
  1225.         car = &gProgram_state.current_car;
  1226.     } else {
  1227.         car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0xff);
  1228.     }
  1229.     model_ptr = car->car_model_actors[(*pChunk)->chunk_data.smudge_data.model_index].actor->model;
  1230.     for (i = 0; i < (*pChunk)->chunk_data.smudge_data.vertex_count; i++) {
  1231.         v = (*pChunk)->chunk_data.smudge_data.vertex_changes[i].vertex_index;
  1232.         inc = (*pChunk)->chunk_data.smudge_data.vertex_changes[i].light_index * pDir;
  1233.         V11MODEL(model_ptr)->groups->vertex_colours[v] = ((V11MODEL(model_ptr)->groups->vertex_colours[v] >> 24) + inc) << 24;
  1234.         if (model_ptr->flags & BR_MODF_UPDATEABLE) {
  1235.             model_ptr->vertices[V11MODEL(model_ptr)->groups->vertex_user[v]].index = (V11MODEL(model_ptr)->groups->vertex_colours[v] >> 24) + inc;
  1236.         }
  1237.     }
  1238. }
  1239.  
  1240. // IDA: void __usercall ApplySmudge(tPipe_chunk **pChunk@<EAX>)
  1241. void ApplySmudge(tPipe_chunk** pChunk) {
  1242.     LOG_TRACE("(%p)", pChunk);
  1243.  
  1244.     DoSmudge(pChunk, 1);
  1245.     AdvanceChunkPtr(pChunk, ePipe_chunk_smudge);
  1246. }
  1247.  
  1248. // IDA: void __usercall ApplyPedestrian(tPipe_chunk **pChunk@<EAX>)
  1249. void ApplyPedestrian(tPipe_chunk** pChunk) {
  1250.     LOG_TRACE("(%p)", pChunk);
  1251.  
  1252.     AdjustPedestrian(
  1253.         (*pChunk)->subject_index,
  1254.         ((*pChunk)->chunk_data.pedestrian_data.action_and_frame_index & 0x70) >> 4,
  1255.         (*pChunk)->chunk_data.pedestrian_data.action_and_frame_index & 0x0f,
  1256.         (*pChunk)->chunk_data.pedestrian_data.hit_points,
  1257.         (*pChunk)->chunk_data.pedestrian_data.action_and_frame_index >> 7,
  1258.         (*pChunk)->chunk_data.pedestrian_data.parent,
  1259.         (*pChunk)->chunk_data.pedestrian_data.parent_actor,
  1260.         (*pChunk)->chunk_data.pedestrian_data.spin_period,
  1261.         (*pChunk)->chunk_data.pedestrian_data.jump_magnitude,
  1262.         &(*pChunk)->chunk_data.pedestrian_data.offset,
  1263.         &(*pChunk)->chunk_data.pedestrian_data.new_translation);
  1264.     AdvanceChunkPtr(pChunk, ePipe_chunk_pedestrian);
  1265. }
  1266.  
  1267. // IDA: void __usercall ApplySpark(tPipe_chunk **pChunk@<EAX>)
  1268. void ApplySpark(tPipe_chunk** pChunk) {
  1269.     LOG_TRACE("(%p)", pChunk);
  1270.  
  1271.     AdjustSpark((*pChunk)->subject_index,
  1272.         &(*pChunk)->chunk_data.spark_data.pos,
  1273.         &(*pChunk)->chunk_data.spark_data.v);
  1274.     AdvanceChunkPtr(pChunk, ePipe_chunk_spark);
  1275. }
  1276.  
  1277. // IDA: void __usercall ApplyShrapnel(tPipe_chunk **pChunk@<EAX>)
  1278. void ApplyShrapnel(tPipe_chunk** pChunk) {
  1279.     LOG_TRACE("(%p)", pChunk);
  1280.  
  1281.     AdjustShrapnel((*pChunk)->subject_index,
  1282.         &(*pChunk)->chunk_data.shrapnel_data.pos,
  1283.         (*pChunk)->chunk_data.shrapnel_data.age,
  1284.         (*pChunk)->chunk_data.shrapnel_data.material);
  1285.     AdvanceChunkPtr(pChunk, ePipe_chunk_shrapnel);
  1286. }
  1287.  
  1288. // IDA: void __usercall ApplyScreenWobble(tPipe_chunk **pChunk@<EAX>)
  1289. void ApplyScreenWobble(tPipe_chunk** pChunk) {
  1290.     LOG_TRACE("(%p)", pChunk);
  1291.  
  1292.     SetScreenWobble((*pChunk)->chunk_data.screen_shake_data.wobble_x,
  1293.         (*pChunk)->chunk_data.screen_shake_data.wobble_y);
  1294.     AdvanceChunkPtr(pChunk, ePipe_chunk_screen_shake);
  1295. }
  1296.  
  1297. // IDA: void __usercall ApplyGrooveStop(tPipe_chunk **pChunk@<EAX>)
  1298. void ApplyGrooveStop(tPipe_chunk** pChunk) {
  1299.     LOG_TRACE("(%p)", pChunk);
  1300.  
  1301.     SetGrooveInterrupt((*pChunk)->subject_index,
  1302.         &(*pChunk)->chunk_data.groove_stop_data.matrix,
  1303.         (*pChunk)->chunk_data.groove_stop_data.path_interrupt,
  1304.         (*pChunk)->chunk_data.groove_stop_data.object_interrupt,
  1305.         (*pChunk)->chunk_data.groove_stop_data.path_resumption,
  1306.         (*pChunk)->chunk_data.groove_stop_data.object_resumption);
  1307.     AdvanceChunkPtr(pChunk, ePipe_chunk_groove_stop);
  1308. }
  1309.  
  1310. // IDA: void __usercall ApplyNonCar(tPipe_chunk **pChunk@<EAX>)
  1311. void ApplyNonCar(tPipe_chunk** pChunk) {
  1312.     LOG_TRACE("(%p)", pChunk);
  1313.  
  1314.     AdjustNonCar((*pChunk)->chunk_data.non_car_data.actor,
  1315.         &(*pChunk)->chunk_data.non_car_data.mat);
  1316.     AdvanceChunkPtr(pChunk, ePipe_chunk_non_car);
  1317. }
  1318.  
  1319. // IDA: void __usercall ApplySmoke(tPipe_chunk **pChunk@<EAX>)
  1320. void ApplySmoke(tPipe_chunk** pChunk) {
  1321.     br_vector3 pos;
  1322.     LOG_TRACE("(%p)", pChunk);
  1323.  
  1324.     GetReducedPos(&pos, &(*pChunk)->chunk_data.smoke_data.pos);
  1325.     AdjustSmoke((*pChunk)->subject_index,
  1326.         (*pChunk)->chunk_data.smoke_data.type,
  1327.         &pos,
  1328.         (*pChunk)->chunk_data.smoke_data.radius / 1024.f,
  1329.         (*pChunk)->chunk_data.smoke_data.strength / 256.f);
  1330.     AdvanceChunkPtr(pChunk, ePipe_chunk_smoke);
  1331. }
  1332.  
  1333. // IDA: void __usercall ApplySmokeColumn(tPipe_chunk **pChunk@<EAX>)
  1334. void ApplySmokeColumn(tPipe_chunk** pChunk) {
  1335.     LOG_TRACE("(%p)", pChunk);
  1336.  
  1337.     AdjustSmokeColumn((*pChunk)->subject_index & 0x3fff,
  1338.         ((((*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),
  1339.         (*pChunk)->chunk_data.smoke_column_data.vertex,
  1340.         (*pChunk)->subject_index >> 14);
  1341.     AdvanceChunkPtr(pChunk, ePipe_chunk_smoke_column);
  1342. }
  1343.  
  1344. // IDA: void __usercall ApplyFlame(tPipe_chunk **pChunk@<EAX>)
  1345. void ApplyFlame(tPipe_chunk** pChunk) {
  1346.     LOG_TRACE("(%p)", pChunk);
  1347.  
  1348.     AdjustFlame((*pChunk)->subject_index,
  1349.         (*pChunk)->chunk_data.flame_data.frame_count,
  1350.         (*pChunk)->chunk_data.flame_data.scale_x,
  1351.         (*pChunk)->chunk_data.flame_data.scale_y,
  1352.         (*pChunk)->chunk_data.flame_data.offset_x,
  1353. #if DETHRACE_FIX_BUGS
  1354.         (*pChunk)->chunk_data.flame_data.offset_z);
  1355. #else
  1356.         (*pChunk)->chunk_data.flame_data.offset_x);
  1357. #endif
  1358.     AdvanceChunkPtr(pChunk, ePipe_chunk_flame);
  1359. }
  1360.  
  1361. // IDA: void __usercall ApplySplash(tPipe_chunk **pChunk@<EAX>)
  1362. void ApplySplash(tPipe_chunk** pChunk) {
  1363.     tCar_spec* c;
  1364.     LOG_TRACE("(%p)", pChunk);
  1365.  
  1366.     if (((*pChunk)->subject_index & 0xff00) == 0) {
  1367.         c = &gProgram_state.current_car;
  1368.     } else {
  1369.         c = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0xff);
  1370.     }
  1371.     c->water_d = (*pChunk)->chunk_data.splash_data.d;
  1372.     BrVector3Copy(&c->water_normal, &(*pChunk)->chunk_data.splash_data.normal);
  1373.     AdvanceChunkPtr(pChunk, ePipe_chunk_splash);
  1374. }
  1375.  
  1376. // IDA: void __usercall ApplyOilSpill(tPipe_chunk **pChunk@<EAX>, tU32 pStop_time@<EDX>)
  1377. void ApplyOilSpill(tPipe_chunk** pChunk, tU32 pStop_time) {
  1378.     LOG_TRACE("(%p, %d)", pChunk, pStop_time);
  1379.  
  1380.     AdjustOilSpill((*pChunk)->subject_index,
  1381.         &(*pChunk)->chunk_data.oil_data.mat,
  1382.         (*pChunk)->chunk_data.oil_data.full_size,
  1383.         (*pChunk)->chunk_data.oil_data.grow_rate,
  1384.         (*pChunk)->chunk_data.oil_data.spill_time,
  1385.         pStop_time,
  1386.         (*pChunk)->chunk_data.oil_data.car,
  1387.         &(*pChunk)->chunk_data.oil_data.original_pos,
  1388.         (*pChunk)->chunk_data.oil_data.pixelmap);
  1389.     AdvanceChunkPtr(pChunk, ePipe_chunk_oil_spill);
  1390. }
  1391.  
  1392. // IDA: void __usercall ApplyFrameBoundary(tPipe_chunk **pChunk@<EAX>)
  1393. void ApplyFrameBoundary(tPipe_chunk** pChunk) {
  1394.     //tU32 result; // Pierre-Marie Baty -- unused variable
  1395.     LOG_TRACE("(%p)", pChunk);
  1396.  
  1397.     gLast_replay_frame_time = (*pChunk)->chunk_data.frame_boundary_data.time;
  1398.     // DAT_0054b244 = PDGetTotalTime();
  1399.     AdvanceChunkPtr(pChunk, ePipe_chunk_frame_boundary);
  1400. }
  1401.  
  1402. // IDA: void __usercall ApplySound(tPipe_chunk **pChunk@<EAX>)
  1403. void ApplySound(tPipe_chunk** pChunk) {
  1404.     LOG_TRACE("(%p)", pChunk);
  1405.  
  1406.     if (!gDisable_sound) {
  1407.         if ((*pChunk)->chunk_data.sound_data.volume == 0) {
  1408.             DRS3StartSound2(GetOutletFromIndex((*pChunk)->chunk_data.sound_data.outlet_index),
  1409.                 (*pChunk)->subject_index,
  1410.                 1,
  1411.                 -1,
  1412.                 -1,
  1413.                 65535.f * GetReplayRate(),
  1414.                 0x10000);
  1415.         } else if (BrVector3LengthSquared(&(*pChunk)->chunk_data.sound_data.position) == 0) {
  1416.             DRS3StartSound2(GetOutletFromIndex((*pChunk)->chunk_data.sound_data.outlet_index),
  1417.                 (*pChunk)->subject_index,
  1418.                 1,
  1419.                 (*pChunk)->chunk_data.sound_data.volume & 0xff,
  1420.                 (*pChunk)->chunk_data.sound_data.volume >> 8,
  1421.                 (float)(*pChunk)->chunk_data.sound_data.pitch * fabsf(GetReplayRate()),
  1422.                 0x10000);
  1423.         } else {
  1424.             DRS3StartSound3D(GetOutletFromIndex((*pChunk)->chunk_data.sound_data.outlet_index),
  1425.                 (*pChunk)->subject_index,
  1426.                 &(*pChunk)->chunk_data.sound_data.position,
  1427.                 &gZero_vector,
  1428.                 1,
  1429.                 (*pChunk)->chunk_data.sound_data.volume,
  1430.                 (float)(*pChunk)->chunk_data.sound_data.pitch * fabsf(GetReplayRate()),
  1431.                 0x10000);
  1432.         }
  1433.     }
  1434.     AdvanceChunkPtr(pChunk, ePipe_chunk_sound);
  1435. }
  1436.  
  1437. // IDA: void __usercall ApplyCar(tPipe_chunk **pChunk@<EAX>)
  1438. void ApplyCar(tPipe_chunk** pChunk) {
  1439.     tCar_spec* car;
  1440.     br_vector3 com_offset_c;
  1441.     br_vector3 com_offset_w;
  1442.     LOG_TRACE("(%p)", pChunk);
  1443.  
  1444.     if (((*pChunk)->subject_index & 0xff00) == 0) {
  1445.         car = &gProgram_state.current_car;
  1446.     } else {
  1447.         car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0x00ff);
  1448.     }
  1449.     BrMatrix34Copy(&car->car_master_actor->t.t.mat, &(*pChunk)->chunk_data.car_data.transformation);
  1450.     BrVector3Copy(&car->v, &(*pChunk)->chunk_data.car_data.velocity);
  1451.     BrMatrix34TApplyV(&car->velocity_car_space, &car->v, &car->car_master_actor->t.t.mat);
  1452.     BrVector3InvScale(&car->velocity_car_space, &car->velocity_car_space, WORLD_SCALE);
  1453.     if (BrVector3LengthSquared(&car->velocity_car_space) >= .0001f) {
  1454.         BrVector3Normalise(&car->direction, &car->v);
  1455.     } else {
  1456.         BrVector3Negate(&car->direction, (br_vector3*)car->car_master_actor->t.t.mat.m[2]);
  1457.     }
  1458.     BrVector3Copy(&car->pos, &car->car_master_actor->t.t.translate.t);
  1459.     BrVector3InvScale(&com_offset_c, &car->cmpos, WORLD_SCALE);
  1460.     BrMatrix34ApplyV(&com_offset_w, &com_offset_c, &car->car_master_actor->t.t.mat);
  1461.     BrVector3Accumulate(&car->pos, &com_offset_w);
  1462.     car->speedo_speed = .07f * (*pChunk)->chunk_data.car_data.speedo_speed / 32767.f;
  1463.     car->lf_sus_position = 0.15f * (*pChunk)->chunk_data.car_data.lf_sus_position / 127.f;
  1464.     car->rf_sus_position = 0.15f * (*pChunk)->chunk_data.car_data.rf_sus_position / 127.f;
  1465.     car->lr_sus_position = 0.15f * (*pChunk)->chunk_data.car_data.lr_sus_position / 127.f;
  1466.     car->rr_sus_position = 0.15f * (*pChunk)->chunk_data.car_data.rr_sus_position / 127.f;
  1467.     car->steering_angle = 60.f * (*pChunk)->chunk_data.car_data.steering_angle / 32767.f;
  1468.     car->revs = 10 * ((*pChunk)->chunk_data.car_data.revs_and_gear & 0x7ff);
  1469.     car->gear = ((*pChunk)->chunk_data.car_data.revs_and_gear >> 12) - 1;
  1470.     car->frame_collision_flag = ((*pChunk)->chunk_data.car_data.revs_and_gear >> 11) & 0x1;
  1471.     AdvanceChunkPtr(pChunk, ePipe_chunk_car);
  1472. }
  1473.  
  1474. // IDA: void __usercall ApplyDamage(tPipe_chunk **pChunk@<EAX>)
  1475. void ApplyDamage(tPipe_chunk** pChunk) {
  1476.     tCar_spec* car;
  1477.     int i;
  1478.     LOG_TRACE("(%p)", pChunk);
  1479.  
  1480.     if (((*pChunk)->subject_index & 0xff00) == 0) {
  1481.         car = &gProgram_state.current_car;
  1482.     } else {
  1483.         car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0x00ff);
  1484.     }
  1485.     for (i = 0; i < COUNT_OF(car->damage_units); i++) {
  1486.         car->damage_units[i].damage_level += (*pChunk)->chunk_data.damage_data.damage_delta[i];
  1487.     }
  1488.     AdvanceChunkPtr(pChunk, ePipe_chunk_damage);
  1489. }
  1490.  
  1491. // IDA: void __usercall ApplySpecial(tPipe_chunk **pChunk@<EAX>)
  1492. void ApplySpecial(tPipe_chunk** pChunk) {
  1493.     LOG_TRACE("(%p)", pChunk);
  1494.  
  1495.     switch ((*pChunk)->subject_index) {
  1496.     case 0:
  1497.         if (fabsf(GetReplayRate()) <= 1.f) {
  1498.             FadePaletteDown();
  1499.         }
  1500.         break;
  1501.     case 1:
  1502.         gPed_scale_factor = 2.0f;
  1503.         break;
  1504.     case 2:
  1505.         gPed_scale_factor = 1.0f;
  1506.         break;
  1507.     case 3:
  1508.         gPed_scale_factor = 0.5f;
  1509.         break;
  1510.     case 4:
  1511.         gPed_scale_factor = 1.0f;
  1512.         break;
  1513.     }
  1514.     AdvanceChunkPtr(pChunk, ePipe_chunk_special);
  1515. }
  1516.  
  1517. // IDA: void __usercall ApplyPedGib(tPipe_chunk **pChunk@<EAX>)
  1518. void ApplyPedGib(tPipe_chunk** pChunk) {
  1519.     LOG_TRACE("(%p)", pChunk);
  1520.  
  1521.     AdjustPedGib((*pChunk)->subject_index,
  1522.         (*pChunk)->chunk_data.ped_gib_data.size,
  1523.         (*pChunk)->chunk_data.ped_gib_data.gib_index,
  1524.         (*pChunk)->chunk_data.ped_gib_data.ped_parent_index,
  1525.         &(*pChunk)->chunk_data.ped_gib_data.transform);
  1526.     AdvanceChunkPtr(pChunk, ePipe_chunk_ped_gib);
  1527. }
  1528.  
  1529. // IDA: void __usercall ApplyProxRay(tPipe_chunk **pChunk@<EAX>)
  1530. void ApplyProxRay(tPipe_chunk** pChunk) {
  1531.     LOG_TRACE("(%p)", pChunk);
  1532.  
  1533.     AdjustProxRay(
  1534.         (*pChunk)->subject_index,
  1535.         (*pChunk)->chunk_data.prox_ray_data.car_ID,
  1536.         (*pChunk)->chunk_data.prox_ray_data.ped_index,
  1537.         (*pChunk)->chunk_data.prox_ray_data.time);
  1538.     AdvanceChunkPtr(pChunk, ePipe_chunk_prox_ray);
  1539. }
  1540.  
  1541. // IDA: void __usercall ApplySkidAdjustment(tPipe_chunk **pChunk@<EAX>)
  1542. void ApplySkidAdjustment(tPipe_chunk** pChunk) {
  1543.     LOG_TRACE("(%p)", pChunk);
  1544.  
  1545.     AdjustSkid((*pChunk)->subject_index,
  1546.         &(*pChunk)->chunk_data.skid_adjustment.matrix,
  1547.         (*pChunk)->chunk_data.skid_adjustment.material_index);
  1548.     AdvanceChunkPtr(pChunk, ePipe_chunk_skid_adjustment);
  1549. }
  1550.  
  1551. // IDA: int __usercall ApplyPipedSession@<EAX>(tU8 **pPtr@<EAX>)
  1552. int ApplyPipedSession(tU8** pPtr) {
  1553.     int i;
  1554.     int return_value;
  1555.     tPipe_chunk* chunk_ptr;
  1556.     tPipe_chunk_type chunk_type;
  1557.     LOG_TRACE("(%p)", pPtr);
  1558.  
  1559.     if (*pPtr == gPipe_record_ptr) {
  1560.         return 1;
  1561.     }
  1562.     gEnd_of_session = *pPtr + (LengthOfSession((tPipe_session*)*pPtr) - sizeof(tU16));
  1563.     REPLAY_DEBUG_ASSERT(((tPipe_session*)*pPtr)->pipe_magic1 == REPLAY_DEBUG_SESSION_MAGIC1);
  1564.     chunk_ptr = (tPipe_chunk*)(*pPtr + offsetof(tPipe_session, chunks));
  1565.     return_value = 0;
  1566.     chunk_type = ((tPipe_session*)*pPtr)->chunk_type;
  1567.     for (i = 0; i < ((tPipe_session*)*pPtr)->number_of_chunks; i++) {
  1568.         switch (chunk_type) {
  1569.         case ePipe_chunk_model_geometry:
  1570.             ApplyModelGeometry(&chunk_ptr);
  1571.             break;
  1572.         case ePipe_chunk_pedestrian:
  1573.             ApplyPedestrian(&chunk_ptr);
  1574.             break;
  1575.         case ePipe_chunk_frame_boundary:
  1576.             ApplyFrameBoundary(&chunk_ptr);
  1577.             return_value = 1;
  1578.             break;
  1579.         case ePipe_chunk_car:
  1580.             ApplyCar(&chunk_ptr);
  1581.             break;
  1582.         case ePipe_chunk_sound:
  1583.             ApplySound(&chunk_ptr);
  1584.             break;
  1585.         case ePipe_chunk_damage:
  1586.             ApplyDamage(&chunk_ptr);
  1587.             break;
  1588.         case ePipe_chunk_special:
  1589.             ApplySpecial(&chunk_ptr);
  1590.             break;
  1591.         case ePipe_chunk_ped_gib:
  1592.             ApplyPedGib(&chunk_ptr);
  1593.             break;
  1594.         case ePipe_chunk_incident:
  1595.             AdvanceChunkPtr(&chunk_ptr, ePipe_chunk_incident);
  1596.             break;
  1597.         case ePipe_chunk_spark:
  1598.             ApplySpark(&chunk_ptr);
  1599.             break;
  1600.         case ePipe_chunk_shrapnel:
  1601.             ApplyShrapnel(&chunk_ptr);
  1602.             break;
  1603.         case ePipe_chunk_screen_shake:
  1604.             ApplyScreenWobble(&chunk_ptr);
  1605.             break;
  1606.         case ePipe_chunk_groove_stop:
  1607.             ApplyGrooveStop(&chunk_ptr);
  1608.             break;
  1609.         case ePipe_chunk_non_car:
  1610.             ApplyNonCar(&chunk_ptr);
  1611.             break;
  1612.         case ePipe_chunk_smoke:
  1613.             ApplySmoke(&chunk_ptr);
  1614.             break;
  1615.         case ePipe_chunk_oil_spill:
  1616.             ApplyOilSpill(&chunk_ptr, 0);
  1617.             break;
  1618.         case ePipe_chunk_smoke_column:
  1619.             ApplySmokeColumn(&chunk_ptr);
  1620.             break;
  1621.         case ePipe_chunk_flame:
  1622.             ApplyFlame(&chunk_ptr);
  1623.             break;
  1624.         case ePipe_chunk_smudge:
  1625.             ApplySmudge(&chunk_ptr);
  1626.             break;
  1627.         case ePipe_chunk_splash:
  1628.             ApplySplash(&chunk_ptr);
  1629.             break;
  1630.         case ePipe_chunk_prox_ray:
  1631.             ApplyProxRay(&chunk_ptr);
  1632.             break;
  1633.         case ePipe_chunk_skid_adjustment:
  1634.             ApplySkidAdjustment(&chunk_ptr);
  1635.             break;
  1636.         default:
  1637.             break;
  1638.         }
  1639.     }
  1640. #if defined(DETHRACE_FIX_BUGS)
  1641.     *pPtr += PIPE_ALIGN(LengthOfSession((tPipe_session*)*pPtr));
  1642. #else
  1643.     *pPtr += LengthOfSession((tPipe_session*)*pPtr);
  1644. #endif
  1645.     if (*pPtr >= gPipe_buffer_working_end && *pPtr != gPipe_record_ptr) {
  1646.         *pPtr = gPipe_buffer_start;
  1647.     }
  1648.     return return_value;
  1649. }
  1650.  
  1651. // IDA: int __usercall MoveSessionPointerBackOne@<EAX>(tU8 **pPtr@<EAX>)
  1652. int MoveSessionPointerBackOne(tU8** pPtr) {
  1653.     LOG_TRACE("(%p)", pPtr);
  1654.  
  1655.     if (*pPtr == gPipe_buffer_oldest && *pPtr != gPipe_record_ptr) {
  1656.         return 1;
  1657.     }
  1658.     if (*pPtr == gPipe_buffer_start) {
  1659.         *pPtr = gPipe_buffer_working_end;
  1660.     }
  1661.     *pPtr -= sizeof(tU16);
  1662.     REPLAY_DEBUG_ASSERT(*(tU16*)*pPtr != 0);
  1663.     *pPtr -= *(tU16*)*pPtr;
  1664.     REPLAY_DEBUG_ASSERT(((tPipe_session*)*pPtr)->pipe_magic1 == REPLAY_DEBUG_SESSION_MAGIC1);
  1665.     return 0;
  1666. }
  1667.  
  1668. // IDA: int __usercall MoveSessionPointerForwardOne@<EAX>(tU8 **pPtr@<EAX>)
  1669. int MoveSessionPointerForwardOne(tU8** pPtr) {
  1670.     LOG_TRACE("(%p)", pPtr);
  1671.  
  1672.     REPLAY_DEBUG_ASSERT(((tPipe_session*)*pPtr)->pipe_magic1 == REPLAY_DEBUG_SESSION_MAGIC1);
  1673. #if defined(DETHRACE_FIX_BUGS)
  1674.     *pPtr += PIPE_ALIGN(LengthOfSession((tPipe_session*)*pPtr));
  1675. #else
  1676.     *pPtr += LengthOfSession((tPipe_session*)*pPtr);
  1677. #endif
  1678.     if (*pPtr >= gPipe_buffer_working_end && *pPtr != gPipe_record_ptr) {
  1679.         *pPtr = gPipe_buffer_start;
  1680.     }
  1681.     return *pPtr == gPipe_record_ptr;
  1682. }
  1683.  
  1684. // IDA: tPipe_chunk* __usercall FindPreviousChunk@<EAX>(tU8 *pPtr@<EAX>, tPipe_chunk_type pType@<EDX>, tChunk_subject_index pIndex@<EBX>)
  1685. tPipe_chunk* FindPreviousChunk(tU8* pPtr, tPipe_chunk_type pType, tChunk_subject_index pIndex) {
  1686.     tU8* ptr;
  1687.     int i;
  1688.     int reached_end;
  1689.     int chunk_counter;
  1690.     tPipe_chunk* mr_chunky;
  1691.     tChunk_subject_index masked_index;
  1692.     LOG_TRACE("(%p, %d, %d)", pPtr, pType, pIndex);
  1693.  
  1694.     ptr = pPtr;
  1695.     chunk_counter = 0;
  1696.     masked_index = pIndex & 0x0fff;
  1697.     while (1) {
  1698.         if (!MoveSessionPointerBackOne(&ptr)) {
  1699.             reached_end = chunk_counter >= gMax_rewind_chunks;
  1700.             chunk_counter++;
  1701.         } else {
  1702.             reached_end = 1;
  1703.         }
  1704.         if (!reached_end) {
  1705.             gEnd_of_session = ptr + LengthOfSession((tPipe_session*)ptr) - sizeof(tU16);
  1706.             mr_chunky = &((tPipe_session*)ptr)->chunks;
  1707.             for (i = 0; i < ((tPipe_session*)ptr)->number_of_chunks && ((tPipe_session*)ptr)->chunk_type == pType; i++) {
  1708.                 if ((mr_chunky->subject_index & 0xfff) == masked_index) {
  1709.                     return mr_chunky;
  1710.                 }
  1711.                 AdvanceChunkPtr(&mr_chunky, pType);
  1712.             }
  1713.         }
  1714.         if (reached_end) {
  1715.             return NULL;
  1716.         }
  1717.     }
  1718. }
  1719.  
  1720. // IDA: void __usercall UndoModelGeometry(tPipe_chunk **pChunk@<EAX>)
  1721. void UndoModelGeometry(tPipe_chunk** pChunk) {
  1722.     int i;
  1723.     br_model* model_ptr;
  1724.     tCar_spec* car;
  1725.     LOG_TRACE("(%p)", pChunk);
  1726.  
  1727.     if (((*pChunk)->subject_index & 0xff00) == 0) {
  1728.         car = &gProgram_state.current_car;
  1729.     } else {
  1730.         car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0x00ff);
  1731.     }
  1732.     model_ptr = car->car_model_actors[(*pChunk)->chunk_data.model_geometry_data.model_index].actor->model;
  1733.     for (i = 0; i < (*pChunk)->chunk_data.model_geometry_data.vertex_count; i++) {
  1734.         BrVector3Sub(&model_ptr->vertices[(*pChunk)->chunk_data.model_geometry_data.vertex_changes[i].vertex_index].p,
  1735.             &model_ptr->vertices[(*pChunk)->chunk_data.model_geometry_data.vertex_changes[i].vertex_index].p,
  1736.             &(*pChunk)->chunk_data.model_geometry_data.vertex_changes[i].delta_coordinates);
  1737.     }
  1738.     SetModelForUpdate(model_ptr, car, 0);
  1739.     AdvanceChunkPtr(pChunk, ePipe_chunk_model_geometry);
  1740. }
  1741.  
  1742. // IDA: void __usercall UndoSmudge(tPipe_chunk **pChunk@<EAX>)
  1743. void UndoSmudge(tPipe_chunk** pChunk) {
  1744.     //int i; // Pierre-Marie Baty -- unused variable
  1745.     //br_model* model_ptr; // Pierre-Marie Baty -- unused variable
  1746.     //tCar_spec* car; // Pierre-Marie Baty -- unused variable
  1747.     LOG_TRACE("(%p)", pChunk);
  1748.  
  1749.     DoSmudge(pChunk, -1);
  1750.     AdvanceChunkPtr(pChunk, ePipe_chunk_smudge);
  1751. }
  1752.  
  1753. // IDA: void __usercall UndoPedestrian(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1754. void UndoPedestrian(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1755.     tPipe_chunk* temp_prev_chunk;
  1756.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1757.  
  1758.     temp_prev_chunk = pPrev_chunk;
  1759.     if (pPrev_chunk == NULL) {
  1760.         ApplyPedestrian(pChunk);
  1761.     } else {
  1762.         gDisable_advance = 1;
  1763.         ApplyPedestrian(&temp_prev_chunk);
  1764.         gDisable_advance = 0;
  1765.         AdvanceChunkPtr(pChunk, ePipe_chunk_pedestrian);
  1766.     }
  1767. }
  1768.  
  1769. // IDA: void __usercall UndoFrameBoundary(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1770. void UndoFrameBoundary(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1771.     //tPipe_chunk* temp_prev_chunk; // Pierre-Marie Baty -- unused variable
  1772.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1773.  
  1774.     ApplyFrameBoundary(pChunk);
  1775.     AdvanceChunkPtr(pChunk, ePipe_chunk_frame_boundary);
  1776. }
  1777.  
  1778. // IDA: void __usercall UndoCar(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1779. void UndoCar(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1780.     tPipe_chunk* temp_prev_chunk;
  1781.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1782.  
  1783.     temp_prev_chunk = pPrev_chunk;
  1784.     if (pPrev_chunk == NULL) {
  1785.         ApplyCar(pChunk);
  1786.     } else {
  1787.         gDisable_advance = 1;
  1788.         ApplyCar(&temp_prev_chunk);
  1789.         gDisable_advance = 0;
  1790.         AdvanceChunkPtr(pChunk, ePipe_chunk_car);
  1791.     }
  1792. }
  1793.  
  1794. // IDA: void __usercall UndoSound(tPipe_chunk **pChunk@<EAX>)
  1795. void UndoSound(tPipe_chunk** pChunk) {
  1796.     LOG_TRACE("(%p)", pChunk);
  1797.  
  1798.     AdvanceChunkPtr(pChunk, ePipe_chunk_sound);
  1799. }
  1800.  
  1801. // IDA: void __usercall UndoDamage(tPipe_chunk **pChunk@<EAX>)
  1802. void UndoDamage(tPipe_chunk** pChunk) {
  1803.     tCar_spec* car;
  1804.     int i;
  1805.     LOG_TRACE("(%p)", pChunk);
  1806.  
  1807.     if (((*pChunk)->subject_index & 0xff00) == 0) {
  1808.         car = &gProgram_state.current_car;
  1809.     } else {
  1810.         car = GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0xff);
  1811.     }
  1812.     for (i = 0; i < COUNT_OF(car->damage_units); i++) {
  1813.         car->damage_units[i].damage_level -= (*pChunk)->chunk_data.damage_data.damage_delta[i];
  1814.     }
  1815.     AdvanceChunkPtr(pChunk, ePipe_chunk_damage);
  1816. }
  1817.  
  1818. // IDA: void __usercall UndoSpecial(tPipe_chunk **pChunk@<EAX>)
  1819. void UndoSpecial(tPipe_chunk** pChunk) {
  1820.     LOG_TRACE("(%p)", pChunk);
  1821.  
  1822.     if ((*pChunk)->subject_index == 0) {
  1823.         ApplySpecial(pChunk);
  1824.     } else {
  1825.         switch ((*pChunk)->subject_index) {
  1826.         case 1:
  1827.             gPed_scale_factor = 1.0f;
  1828.             break;
  1829.         case 2:
  1830.             gPed_scale_factor = 2.0f;
  1831.             break;
  1832.         case 3:
  1833.             gPed_scale_factor = 1.0f;
  1834.             break;
  1835.         case 4:
  1836.             gPed_scale_factor = 0.5f;
  1837.             break;
  1838.         }
  1839.         AdvanceChunkPtr(pChunk, ePipe_chunk_special);
  1840.     }
  1841. }
  1842.  
  1843. // IDA: void __usercall UndoPedGib(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1844. void UndoPedGib(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1845.     tPipe_chunk* temp_prev_chunk;
  1846.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1847.  
  1848.     temp_prev_chunk = pPrev_chunk;
  1849.     gDisable_advance = 1;
  1850.     if (pPrev_chunk != NULL) {
  1851.         ApplyPedGib(&temp_prev_chunk);
  1852.     }
  1853.     gDisable_advance = 0;
  1854.     AdvanceChunkPtr(pChunk, ePipe_chunk_ped_gib);
  1855. }
  1856.  
  1857. // IDA: void __usercall UndoSpark(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1858. void UndoSpark(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1859.     tPipe_chunk* temp_prev_chunk;
  1860.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1861.  
  1862.     temp_prev_chunk = pPrev_chunk;
  1863.     gDisable_advance = 1;
  1864.     if (pPrev_chunk != NULL) {
  1865.         ApplySpark(&temp_prev_chunk);
  1866.     }
  1867.     gDisable_advance = 0;
  1868.     AdvanceChunkPtr(pChunk, ePipe_chunk_spark);
  1869. }
  1870.  
  1871. // IDA: void __usercall UndoShrapnel(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1872. void UndoShrapnel(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1873.     tPipe_chunk* temp_prev_chunk;
  1874.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1875.  
  1876.     temp_prev_chunk = pPrev_chunk;
  1877.     gDisable_advance = 1;
  1878.     if (pPrev_chunk != NULL) {
  1879.         ApplyShrapnel(&temp_prev_chunk);
  1880.     }
  1881.     gDisable_advance = 0;
  1882.     AdvanceChunkPtr(pChunk, ePipe_chunk_shrapnel);
  1883. }
  1884.  
  1885. // IDA: void __usercall UndoScreenWobble(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1886. void UndoScreenWobble(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1887.     tPipe_chunk* temp_prev_chunk;
  1888.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1889.  
  1890.     temp_prev_chunk = pPrev_chunk;
  1891.     gDisable_advance = 1;
  1892.     if (pPrev_chunk == NULL) {
  1893.         SetScreenWobble(0, 0);
  1894.     } else {
  1895.         ApplyScreenWobble(&temp_prev_chunk);
  1896.     }
  1897.     gDisable_advance = 0;
  1898.     AdvanceChunkPtr(pChunk, ePipe_chunk_screen_shake);
  1899. }
  1900.  
  1901. // IDA: void __usercall UndoGrooveStop(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1902. void UndoGrooveStop(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1903.     tPipe_chunk* temp_prev_chunk;
  1904.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1905.  
  1906.     temp_prev_chunk = pPrev_chunk;
  1907.     gDisable_advance = 1;
  1908.     if (pPrev_chunk != NULL) {
  1909.         ApplyGrooveStop(&temp_prev_chunk);
  1910.     }
  1911.     gDisable_advance = 0;
  1912.     AdvanceChunkPtr(pChunk, ePipe_chunk_groove_stop);
  1913. }
  1914.  
  1915. // IDA: void __usercall UndoNonCar(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1916. void UndoNonCar(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1917.     tPipe_chunk* temp_prev_chunk;
  1918.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1919.  
  1920.     temp_prev_chunk = pPrev_chunk;
  1921.     gDisable_advance = 1;
  1922.     if (pPrev_chunk != NULL) {
  1923.         ApplyNonCar(&temp_prev_chunk);
  1924.     }
  1925.     gDisable_advance = 0;
  1926.     AdvanceChunkPtr(pChunk, ePipe_chunk_non_car);
  1927. }
  1928.  
  1929. // IDA: void __usercall UndoSmoke(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1930. void UndoSmoke(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1931.     tPipe_chunk* temp_prev_chunk;
  1932.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1933.  
  1934.     temp_prev_chunk = pPrev_chunk;
  1935.     gDisable_advance = 1;
  1936.     if (pPrev_chunk != NULL) {
  1937.         ApplySmoke(&temp_prev_chunk);
  1938.     }
  1939.     gDisable_advance = 0;
  1940.     AdvanceChunkPtr(pChunk, ePipe_chunk_smoke);
  1941. }
  1942.  
  1943. // IDA: void __usercall UndoSmokeColumn(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1944. void UndoSmokeColumn(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1945.     //tPipe_chunk* temp_prev_chunk; // Pierre-Marie Baty -- unused variable
  1946.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1947.  
  1948.     ApplySmokeColumn(pChunk);
  1949. }
  1950.  
  1951. // IDA: void __usercall UndoFlame(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1952. void UndoFlame(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1953.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1954.  
  1955.     ApplyFlame(pChunk);
  1956. }
  1957.  
  1958. // IDA: void __usercall UndoSplash(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1959. void UndoSplash(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1960.     tPipe_chunk* temp_prev_chunk;
  1961.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1962.  
  1963.     temp_prev_chunk = pPrev_chunk;
  1964.     gDisable_advance = 1;
  1965.     if (pPrev_chunk == NULL) {
  1966.         ((((*pChunk)->subject_index & 0xff00) == 0) ? &gProgram_state.current_car : GetCarSpec((*pChunk)->subject_index >> 8, (*pChunk)->subject_index & 0xff))->water_d = 10000.f;
  1967.     } else {
  1968.         ApplySplash(&temp_prev_chunk);
  1969.     }
  1970.     gDisable_advance = 0;
  1971.     AdvanceChunkPtr(pChunk, ePipe_chunk_splash);
  1972. }
  1973.  
  1974. // IDA: void __usercall UndoOilSpill(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1975. void UndoOilSpill(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1976.     tPipe_chunk* temp_prev_chunk;
  1977.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1978.  
  1979.     temp_prev_chunk = pPrev_chunk;
  1980.     gDisable_advance = 1;
  1981.     if (pPrev_chunk != NULL) {
  1982.         ApplyOilSpill(&temp_prev_chunk, (*pChunk)->chunk_data.oil_data.previous_stop_time);
  1983.     }
  1984.     gDisable_advance = 0;
  1985.     AdvanceChunkPtr(pChunk, ePipe_chunk_oil_spill);
  1986. }
  1987.  
  1988. // IDA: void __usercall UndoProxRay(tPipe_chunk **pChunk@<EAX>)
  1989. void UndoProxRay(tPipe_chunk** pChunk) {
  1990.     LOG_TRACE("(%p)", pChunk);
  1991.  
  1992.     ApplyProxRay(pChunk);
  1993. }
  1994.  
  1995. // IDA: void __usercall UndoSkidAdjustment(tPipe_chunk **pChunk@<EAX>, tPipe_chunk *pPrev_chunk@<EDX>)
  1996. void UndoSkidAdjustment(tPipe_chunk** pChunk, tPipe_chunk* pPrev_chunk) {
  1997.     LOG_TRACE("(%p, %p)", pChunk, pPrev_chunk);
  1998.  
  1999.     gDisable_advance = 1;
  2000.     if (pPrev_chunk == NULL) {
  2001.         HideSkid((*pChunk)->subject_index);
  2002.     } else {
  2003.         ApplySkidAdjustment(&pPrev_chunk);
  2004.     }
  2005.     gDisable_advance = 0;
  2006.     AdvanceChunkPtr(pChunk, ePipe_chunk_skid_adjustment);
  2007. }
  2008.  
  2009. // IDA: int __usercall UndoPipedSession@<EAX>(tU8 **pPtr@<EAX>)
  2010. int UndoPipedSession(tU8** pPtr) {
  2011.     tPipe_chunk* chunk_ptr;
  2012.     tPipe_chunk* prev_chunk;
  2013.     tU8* temp_ptr;
  2014.     tU8* pushed_end_of_session;
  2015.     int i;
  2016.     tPipe_chunk_type chunk_type;
  2017.     LOG_TRACE("(%p)", pPtr);
  2018.  
  2019.     if (MoveSessionPointerBackOne(pPtr)) {
  2020.         return 1;
  2021.     }
  2022.     REPLAY_DEBUG_ASSERT(((tPipe_session*)*pPtr)->pipe_magic1 == REPLAY_DEBUG_SESSION_MAGIC1);
  2023.     gEnd_of_session = *pPtr + LengthOfSession((tPipe_session*)*pPtr) - sizeof(tU16);
  2024.     chunk_ptr = &((tPipe_session*)*pPtr)->chunks;
  2025.     chunk_type = ((tPipe_session*)*pPtr)->chunk_type;
  2026.     pushed_end_of_session = gEnd_of_session;
  2027.     for (i = 0; i < ((tPipe_session*)*pPtr)->number_of_chunks; i++) {
  2028.         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)) {
  2029.             prev_chunk = FindPreviousChunk(*pPtr, ((tPipe_session*)*pPtr)->chunk_type, chunk_ptr->subject_index);
  2030.         }
  2031.         REPLAY_DEBUG_ASSERT(((tPipe_chunk*)chunk_ptr)->chunk_magic1 == REPLAY_DEBUG_CHUNK_MAGIC1);
  2032.         gEnd_of_session = pushed_end_of_session;
  2033.         switch (chunk_type) {
  2034.         case ePipe_chunk_model_geometry:
  2035.             UndoModelGeometry(&chunk_ptr);
  2036.             break;
  2037.         case ePipe_chunk_pedestrian:
  2038.             UndoPedestrian(&chunk_ptr, prev_chunk);
  2039.             break;
  2040.         case ePipe_chunk_frame_boundary:
  2041.             UndoFrameBoundary(&chunk_ptr, prev_chunk);
  2042.             break;
  2043.         case ePipe_chunk_car:
  2044.             UndoCar(&chunk_ptr, prev_chunk);
  2045.             break;
  2046.         case ePipe_chunk_sound:
  2047.             UndoSound(&chunk_ptr);
  2048.             break;
  2049.         case ePipe_chunk_damage:
  2050.             UndoDamage(&chunk_ptr);
  2051.             break;
  2052.         case ePipe_chunk_special:
  2053.             UndoSpecial(&chunk_ptr);
  2054.             break;
  2055.         case ePipe_chunk_ped_gib:
  2056.             UndoPedGib(&chunk_ptr, prev_chunk);
  2057.             break;
  2058.         case ePipe_chunk_incident:
  2059.             AdvanceChunkPtr(&chunk_ptr, ePipe_chunk_incident);
  2060.             break;
  2061.         case ePipe_chunk_spark:
  2062.             UndoSpark(&chunk_ptr, prev_chunk);
  2063.             break;
  2064.         case ePipe_chunk_shrapnel:
  2065.             UndoShrapnel(&chunk_ptr, prev_chunk);
  2066.             break;
  2067.         case ePipe_chunk_screen_shake:
  2068.             UndoScreenWobble(&chunk_ptr, prev_chunk);
  2069.             break;
  2070.         case ePipe_chunk_groove_stop:
  2071.             UndoGrooveStop(&chunk_ptr, prev_chunk);
  2072.             break;
  2073.         case ePipe_chunk_non_car:
  2074.             UndoNonCar(&chunk_ptr, prev_chunk);
  2075.             break;
  2076.         case ePipe_chunk_smoke:
  2077.             UndoSmoke(&chunk_ptr, prev_chunk);
  2078.             break;
  2079.         case ePipe_chunk_oil_spill:
  2080.             UndoOilSpill(&chunk_ptr, prev_chunk);
  2081.             break;
  2082.         case ePipe_chunk_smoke_column:
  2083.             UndoSmokeColumn(&chunk_ptr, prev_chunk);
  2084.             break;
  2085.         case ePipe_chunk_flame:
  2086.             UndoFlame(&chunk_ptr, prev_chunk);
  2087.             break;
  2088.         case ePipe_chunk_smudge:
  2089.             UndoSmudge(&chunk_ptr);
  2090.             break;
  2091.         case ePipe_chunk_splash:
  2092.             UndoSplash(&chunk_ptr, prev_chunk);
  2093.             break;
  2094.         case ePipe_chunk_prox_ray:
  2095.             UndoProxRay(&chunk_ptr);
  2096.             break;
  2097.         case ePipe_chunk_skid_adjustment:
  2098.             UndoSkidAdjustment(&chunk_ptr, prev_chunk);
  2099.             break;
  2100.         default:
  2101.             break;
  2102.         }
  2103.     }
  2104.     temp_ptr = *pPtr;
  2105.     if (MoveSessionPointerBackOne(&temp_ptr)) {
  2106.         return 1;
  2107.     }
  2108.     return ((tPipe_session*)temp_ptr)->chunk_type == ePipe_chunk_frame_boundary;
  2109. }
  2110.  
  2111. // IDA: tU32 __usercall FindPrevFrameTime@<EAX>(tU8 *pPtr@<EAX>)
  2112. tU32 FindPrevFrameTime(tU8* pPtr) {
  2113.     tU8* temp_ptr;
  2114.     LOG_TRACE("(%p)", pPtr);
  2115.  
  2116.     temp_ptr = pPtr;
  2117.     do {
  2118.         if (MoveSessionPointerBackOne(&temp_ptr)) {
  2119.             return 0;
  2120.         }
  2121.     } while (((tPipe_session*)temp_ptr)->chunk_type != ePipe_chunk_frame_boundary);
  2122.     return ((tPipe_session*)temp_ptr)->chunks.chunk_data.frame_boundary_data.time;
  2123. }
  2124.  
  2125. // IDA: void __usercall ScanBuffer(tU8 **pPtr@<EAX>, tPipe_chunk_type pType@<EDX>, tU32 pDefault_time@<EBX>, int (*pCall_back)(tPipe_chunk*, int, tU32)@<ECX>, int (*pTime_check)(tU32))
  2126. void ScanBuffer(tU8** pPtr, tPipe_chunk_type pType, tU32 pDefault_time, int (*pCall_back)(tPipe_chunk*, int, tU32), int (*pTime_check)(tU32)) {
  2127.     //tPipe_chunk* chunk_ptr; // Pierre-Marie Baty -- unused variable
  2128.     tU32 the_time;
  2129.     LOG_TRACE("(%p, %d, %d, %p, %p)", pPtr, pType, pDefault_time, pCall_back, pTime_check);
  2130.  
  2131.     the_time = pDefault_time;
  2132.     while (1) {
  2133.         if (PipeSearchForwards() ? MoveSessionPointerForwardOne(pPtr) : MoveSessionPointerBackOne(pPtr)) {
  2134.             return;
  2135.         }
  2136.         gEnd_of_session = *pPtr + LengthOfSession((tPipe_session*)*pPtr) - sizeof(tU16);
  2137.         if (((tPipe_session*)*pPtr)->chunk_type == ePipe_chunk_frame_boundary) {
  2138.             the_time = ((tPipe_session*)*pPtr)->chunks.chunk_data.frame_boundary_data.time;
  2139.         } else if (((tPipe_session*)*pPtr)->chunk_type == pType) {
  2140.             if (pCall_back(&((tPipe_session*)*pPtr)->chunks, ((tPipe_session*)*pPtr)->number_of_chunks, the_time)) {
  2141.                 return;
  2142.             }
  2143.         }
  2144.         if (pTime_check != NULL) {
  2145.             if (!pTime_check(the_time)) {
  2146.                 return;
  2147.             }
  2148.         }
  2149.     }
  2150. }
  2151.  
  2152. // IDA: int __usercall CheckSound@<EAX>(tPipe_chunk *pChunk_ptr@<EAX>, int pChunk_count@<EDX>, tU32 pTime@<EBX>)
  2153. int CheckSound(tPipe_chunk* pChunk_ptr, int pChunk_count, tU32 pTime) {
  2154.     //int i; // Pierre-Marie Baty -- unused variable
  2155.     //int sound_length; // Pierre-Marie Baty -- unused variable
  2156.     //tPipe_chunk* temp_ptr; // Pierre-Marie Baty -- unused variable
  2157.     LOG_TRACE("(%p, %d, %d)", pChunk_ptr, pChunk_count, pTime);
  2158.  
  2159.     STUB_ONCE();
  2160.     return 1;
  2161. }
  2162.  
  2163. // IDA: int __usercall SoundTimeout@<EAX>(tU32 pTime@<EAX>)
  2164. int SoundTimeout(tU32 pTime) {
  2165.     LOG_TRACE("(%d)", pTime);
  2166.     NOT_IMPLEMENTED();
  2167. }
  2168.  
  2169. // IDA: void __usercall ScanAndPlaySoundsToBe(tU8 *pPtr@<EAX>, tU32 pOldest_time@<EDX>, tU32 pYoungest_time@<EBX>)
  2170. void ScanAndPlaySoundsToBe(tU8* pPtr, tU32 pOldest_time, tU32 pYoungest_time) {
  2171.     //tU8* temp_ptr; // Pierre-Marie Baty -- unused variable
  2172.     LOG_TRACE("(%p, %d, %d)", pPtr, pOldest_time, pYoungest_time);
  2173.     NOT_IMPLEMENTED();
  2174. }
  2175.  
  2176. // IDA: int __usercall CheckCar@<EAX>(tPipe_chunk *pChunk_ptr@<EAX>, int pChunk_count@<EDX>, tU32 pTime@<EBX>)
  2177. int CheckCar(tPipe_chunk* pChunk_ptr, int pChunk_count, tU32 pTime) {
  2178.     int i;
  2179.     tCar_spec* car;
  2180.     br_vector3 com_offset_c;
  2181.     br_vector3 com_offset_w;
  2182.     br_vector3 difference;
  2183.     tPipe_chunk* temp_ptr;
  2184.     LOG_TRACE("(%p, %d, %d)", pChunk_ptr, pChunk_count, pTime);
  2185.  
  2186.     temp_ptr = pChunk_ptr;
  2187.     if (PipeSearchForwards()) {
  2188.         if (pTime <= gOldest_time) {
  2189.             return 0;
  2190.         }
  2191.     } else {
  2192.         if (pTime >= gOldest_time) {
  2193.             return 0;
  2194.         }
  2195.     }
  2196.     for (i = 0; i < pChunk_count; i++) {
  2197.         if ((temp_ptr->subject_index & 0xff00) == 0) {
  2198.             car = &gProgram_state.current_car;
  2199.         } else {
  2200.             car = GetCarSpec(temp_ptr->subject_index >> 8, temp_ptr->subject_index & 0xff);
  2201.         }
  2202.         if (car == gCar_ptr) {
  2203.             BrVector3Copy(&gCar_pos, (br_vector3*)temp_ptr->chunk_data.car_data.transformation.m[3]);
  2204.             BrVector3InvScale(&com_offset_c, &car->cmpos, WORLD_SCALE);
  2205.             BrMatrix34ApplyV(&com_offset_w, &com_offset_c, &temp_ptr->chunk_data.car_data.transformation);
  2206.             BrVector3Accumulate(&gCar_pos, &com_offset_w);
  2207.             BrVector3Sub(&difference, &gCar_pos, &gReference_pos);
  2208.             if (BrVector3LengthSquared(&difference) <= gMax_distance) {
  2209.                 gTrigger_time = pTime;
  2210.                 return 0;
  2211.             } else {
  2212.                 gTrigger_time = pTime;
  2213.                 return 1;
  2214.             }
  2215.         }
  2216.         AdvanceChunkPtr(&temp_ptr, ePipe_chunk_car);
  2217.     }
  2218.     return 0;
  2219. }
  2220.  
  2221. // IDA: int __usercall CarTimeout@<EAX>(tU32 pTime@<EAX>)
  2222. int CarTimeout(tU32 pTime) {
  2223.     LOG_TRACE("(%d)", pTime);
  2224.  
  2225.     if (PipeSearchForwards()) {
  2226.         if (pTime > gYoungest_time) {
  2227.             return 0;
  2228.         }
  2229.     } else {
  2230.         if (pTime < gYoungest_time) {
  2231.             return 0;
  2232.         }
  2233.     }
  2234.     return 1;
  2235. }
  2236.  
  2237. // IDA: void __usercall ScanCarsPositions(tCar_spec *pCar@<EAX>, br_vector3 *pSource_pos@<EDX>, br_scalar pMax_distance_sqr, tU32 pOffset_time, tU32 pTime_period, br_vector3 *pCar_pos, tU32 *pTime_returned)
  2238. 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) {
  2239.     tU8* temp_ptr;
  2240.     LOG_TRACE("(%p, %p, %f, %d, %d, %p, %p)", pCar, pSource_pos, pMax_distance_sqr, pOffset_time, pTime_period, pCar_pos, pTime_returned);
  2241.  
  2242.     temp_ptr = gPipe_play_ptr;
  2243.     gTrigger_time = 0;
  2244.     gMax_distance = pMax_distance_sqr;
  2245.     BrVector3Copy(&gReference_pos, pSource_pos);
  2246.     gCar_ptr = pCar;
  2247.  
  2248.     if (PipeSearchForwards()) {
  2249.         gOldest_time = GetTotalTime() + pOffset_time;
  2250.         gYoungest_time = gOldest_time + pTime_period;
  2251.     } else {
  2252.         gOldest_time = GetTotalTime() - pOffset_time;
  2253.         gYoungest_time = gOldest_time - pTime_period;
  2254.     }
  2255.  
  2256.     ScanBuffer(&temp_ptr, ePipe_chunk_car, GetTotalTime(), CheckCar, CarTimeout);
  2257.     BrVector3Copy(pCar_pos, &gCar_pos);
  2258.     if (pCar_pos->v[0] > 500.f) {
  2259.         Vector3AddFloats(pCar_pos, pCar_pos, -1000.f, -1000.f, -1000.f);
  2260.     }
  2261.     *pTime_returned = gTrigger_time;
  2262. }
  2263.  
  2264. // IDA: int __usercall CheckIncident@<EAX>(tPipe_chunk *pChunk_ptr@<EAX>, int pChunk_count@<EDX>, tU32 pTime@<EBX>)
  2265. int CheckIncident(tPipe_chunk* pChunk_ptr, int pChunk_count, tU32 pTime) {
  2266.     LOG_TRACE("(%p, %d, %d)", pChunk_ptr, pChunk_count, pTime);
  2267.  
  2268.     if (PipeSearchForwards()) {
  2269.         if (pTime <= gOldest_time) {
  2270.             return 0;
  2271.         }
  2272.     } else {
  2273.         if (gOldest_time <= pTime) {
  2274.             return 0;
  2275.         }
  2276.     }
  2277.     gIncidentChunk = pChunk_ptr;
  2278.     gTrigger_time = pTime;
  2279.     return 1;
  2280. }
  2281.  
  2282. // IDA: int __usercall GetNextIncident@<EAX>(tU32 pOffset_time@<EAX>, tIncident_type *pIncident_type@<EDX>, float *pSeverity@<EBX>, tIncident_info *pInfo@<ECX>, tU32 *pTime_away)
  2283. int GetNextIncident(tU32 pOffset_time, tIncident_type* pIncident_type, float* pSeverity, tIncident_info* pInfo, tU32* pTime_away) {
  2284.     tU8* temp_ptr;
  2285.     LOG_TRACE("(%d, %p, %p, %p, %p)", pOffset_time, pIncident_type, pSeverity, pInfo, pTime_away);
  2286.  
  2287.     temp_ptr = gPipe_play_ptr;
  2288.     gTrigger_time = 0;
  2289.     if (PipeSearchForwards()) {
  2290.         gOldest_time = GetTotalTime() + pOffset_time;
  2291.     } else {
  2292.         gOldest_time = GetTotalTime() - pOffset_time;
  2293.     }
  2294.     ScanBuffer(&temp_ptr, ePipe_chunk_incident, GetTotalTime(), CheckIncident, NULL);
  2295.     if (gTrigger_time != 0) {
  2296.         *pTime_away = gTrigger_time - GetTotalTime();
  2297.         *pIncident_type = gIncidentChunk->subject_index;
  2298.         *pSeverity = gIncidentChunk->chunk_data.incident_data.severity;
  2299.         if (*pIncident_type == eIncident_ped) {
  2300.             pInfo->ped_info.ped_actor = GetPedestrianActor(gIncidentChunk->chunk_data.incident_data.info.ped_info.ped_index);
  2301.             pInfo->ped_info.murderer_actor = gIncidentChunk->chunk_data.incident_data.info.ped_info.actor;
  2302.         } else if (*pIncident_type == eIncident_car) {
  2303.             if ((gIncidentChunk->chunk_data.incident_data.info.car_info.car_ID & 0xff00) == 0) {
  2304.                 pInfo->car_info.car = &gProgram_state.current_car;
  2305.             } else {
  2306.                 pInfo->car_info.car = GetCarSpec(gIncidentChunk->chunk_data.incident_data.info.car_info.car_ID >> 8,
  2307.                     gIncidentChunk->chunk_data.incident_data.info.car_info.car_ID & 0xff);
  2308.             }
  2309.             BrVector3Copy(&pInfo->car_info.impact_point, &gIncidentChunk->chunk_data.incident_data.info.car_info.impact_point);
  2310.         } else if (*pIncident_type == eIncident_wall) {
  2311.             BrVector3Copy(&pInfo->wall_info.pos, &gIncidentChunk->chunk_data.incident_data.info.wall_info.pos);
  2312.         }
  2313.     }
  2314.     return gTrigger_time;
  2315. }
  2316.  
  2317. // IDA: tU32 __cdecl GetARStartTime()
  2318. tU32 GetARStartTime(void) {
  2319.     tU8* temp_ptr;
  2320.     LOG_TRACE("()");
  2321.  
  2322.     temp_ptr = gPipe_buffer_oldest;
  2323.     do {
  2324.         if (MoveSessionPointerForwardOne(&temp_ptr)) {
  2325.             return 0;
  2326.         }
  2327.     } while (((tPipe_session*)temp_ptr)->chunk_type != ePipe_chunk_frame_boundary);
  2328.     return ((tPipe_session*)temp_ptr)->chunks.chunk_data.frame_boundary_data.time;
  2329. }
  2330.