Subversion Repositories Games.Carmageddon

Rev

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

  1. #include "racestrt.h"
  2. #include "brender/brender.h"
  3. #include "cutscene.h"
  4. #include "displays.h"
  5. #include "drmem.h"
  6. #include "errors.h"
  7. #include "flicplay.h"
  8. #include "globvars.h"
  9. #include "globvrpb.h"
  10. #include "grafdata.h"
  11. #include "graphics.h"
  12. #include "harness/config.h"
  13. #include "harness/trace.h"
  14. #include "input.h"
  15. #include "intrface.h"
  16. #include "loading.h"
  17. #include "netgame.h"
  18. #include "network.h"
  19. #include "newgame.h"
  20. #include "opponent.h"
  21. #include "pd/sys.h"
  22. #include "racestrt.h"
  23. #include "sound.h"
  24. #include "structur.h"
  25. #include "utility.h"
  26. #include "world.h"
  27. #include <stdlib.h>
  28.  
  29. int gGrid_number_colour[4] = { 49u, 201u, 1u, 201u };
  30. int gJust_bought_part;
  31. tU32 gLast_host_query;
  32. br_pixelmap* gDead_car;
  33. int gFade_away_parts_shop;
  34. tU32 gDare_start_time;
  35. int gRefund_rate;
  36. int gSwap_grid_2;
  37. int gSwap_grid_1;
  38. int gChange_race_net_mode;
  39. tParts_category gPart_category;
  40. tU32 gNet_synch_start;
  41. tNet_game_details* gChoose_car_net_game;
  42. int gPart_index;
  43. int gChallenger_index__racestrt;                 // suffix added to avoid duplicate symbol
  44. tGrid_draw gDraw_grid_status;
  45. tNet_sequence_type gNet_race_sequence__racestrt; // suffix added to avoid duplicate symbol
  46. br_pixelmap* gTaken_image;
  47. int gGrid_number_x_coords[31];
  48. int gGrid_transition_stage;
  49. int gGrid_y_adjust;
  50. br_pixelmap* gBullet_image;
  51. br_pixelmap* gDeceased_image;
  52. int gBest_pos_available;
  53. int gChallenger_position;
  54. int gOpponent_index;
  55. int gChallenge_time;
  56. int gOriginal_position;
  57. int gCurrent_race_index;
  58. tInterface_spec* gStart_interface_spec;
  59. int gCurrent_car_index;
  60. int gOur_starting_position;
  61.  
  62. // IDA: void __usercall DrawRaceList(int pOffset@<EAX>)
  63. void DrawRaceList(int pOffset) {
  64.     int i;
  65.     //int font_height; // Pierre-Marie Baty -- unused variable
  66.     int pitch;
  67.     int y;
  68.     int left_most;
  69.     int right_most;
  70.     int rank_colour;
  71.     int text_colour;
  72.     //int box_bot; // Pierre-Marie Baty -- unused variable
  73.     //int text_width; // Pierre-Marie Baty -- unused variable
  74.     int text_x;
  75.     char rank_str[256];
  76.     LOG_TRACE("(%d)", pOffset);
  77.  
  78.     left_most = (gCurrent_graf_data->choose_race_rank_right - 2 * gBig_font->width[48] + gCurrent_graf_data->choose_race_left) / 2;
  79.     right_most = gCurrent_graf_data->choose_race_right - (left_most - gCurrent_graf_data->choose_race_left);
  80.     BrPixelmapRectangleFill(gBack_screen,
  81.         left_most,
  82.         gCurrent_graf_data->choose_race_y_top,
  83.         gCurrent_graf_data->choose_race_right - gCurrent_graf_data->choose_race_left - 2 * (left_most - gCurrent_graf_data->choose_race_left),
  84.         gCurrent_graf_data->choose_race_y_bottom - gCurrent_graf_data->choose_race_y_top,
  85.         0);
  86.     pitch = gCurrent_graf_data->choose_race_y_pitch;
  87.     for (i = 0; i < gNumber_of_races; i++) {
  88.         y = pitch * i + gCurrent_graf_data->choose_race_curr_y - pOffset;
  89.         if (gCurrent_graf_data->choose_race_y_top <= (y - (TranslationMode() ? 2 : 0)) && (y + gBig_font->glyph_y) < gCurrent_graf_data->choose_race_line_y) {
  90.             if ((gProgram_state.rank > gRace_list[i].rank_required || gProgram_state.rank < gRace_list[i].best_rank) && !gProgram_state.game_completed && !gChange_race_net_mode) {
  91.                 rank_colour = 44;
  92.                 text_colour = 49;
  93.             } else if (gCurrent_graf_data->choose_race_curr_y == y) {
  94.                 rank_colour = 5;
  95.                 text_colour = 1;
  96.             } else {
  97.                 rank_colour = 198;
  98.                 text_colour = 201;
  99.             }
  100.             if (!gChange_race_net_mode) {
  101.                 sprintf(rank_str, "%2d", gRace_list[i].rank_required);
  102.                 TransBrPixelmapText(gBack_screen,
  103.                     gCurrent_graf_data->choose_race_rank_right - BrPixelmapTextWidth(gBack_screen, gBig_font, rank_str),
  104.                     y,
  105.                     rank_colour,
  106.                     gBig_font,
  107.                     rank_str);
  108.             }
  109.             TransBrPixelmapText(gBack_screen,
  110.                 gCurrent_graf_data->choose_race_name_left,
  111.                 y,
  112.                 text_colour,
  113.                 gBig_font,
  114.                 gRace_list[i].name);
  115.             if (gRace_list[i].been_there_done_that && gBullet_image != NULL) {
  116.                 BrPixelmapRectangleCopy(gBack_screen,
  117.                     gCurrent_graf_data->choose_race_bullet_left,
  118.                     y + (gBig_font->glyph_y - gBullet_image->height) / 2,
  119.                     gBullet_image,
  120.                     0,
  121.                     0,
  122.                     gBullet_image->width,
  123.                     gBullet_image->height);
  124.             }
  125.         }
  126.     }
  127.     if (gChange_race_net_mode) {
  128.         strcpy(rank_str, GetMiscString(gNet_race_sequence__racestrt == 1 ? kMiscString_SUBSEQUENT_RACES_WILL_BE_RANDOM : kMiscString_RACES_WILL_CONTINUE_DOWN_THIS_LIST));
  129.         TransBrPixelmapText(gBack_screen,
  130.             (right_most + left_most - BrPixelmapTextWidth(gBack_screen, gBig_font, rank_str)) / 2,
  131.             gCurrent_graf_data->choose_race_current_text_y,
  132.             5,
  133.             gBig_font,
  134.             rank_str);
  135.     } else {
  136.         sprintf(rank_str, "%s%d", GetMiscString(kMiscString_YourCurrentRankIs), gProgram_state.rank);
  137.         text_x = (gCurrent_graf_data->choose_race_left + gCurrent_graf_data->choose_race_right) / 2 - BrPixelmapTextWidth(gBack_screen, gBig_font, rank_str) / 2;
  138.         TransBrPixelmapText(gBack_screen,
  139.             text_x,
  140.             gCurrent_graf_data->choose_race_current_text_y,
  141.             3,
  142.             gBig_font,
  143.             GetMiscString(kMiscString_YourCurrentRankIs));
  144.         sprintf(rank_str, "%d", gProgram_state.rank);
  145.         TransBrPixelmapText(gBack_screen,
  146.             text_x + BrPixelmapTextWidth(gBack_screen, gBig_font, GetMiscString(kMiscString_YourCurrentRankIs)),
  147.             gCurrent_graf_data->choose_race_current_text_y,
  148.             5,
  149.             gBig_font,
  150.             rank_str);
  151.     }
  152.     BrPixelmapLine(gBack_screen,
  153.         left_most,
  154.         gCurrent_graf_data->choose_race_line_y,
  155.         right_most,
  156.         gCurrent_graf_data->choose_race_line_y,
  157.         6);
  158.     DrawRectangle(gBack_screen,
  159.         left_most,
  160.         gCurrent_graf_data->choose_race_box_top - 1,
  161.         right_most,
  162.         2 * gCurrent_graf_data->choose_race_curr_y - gCurrent_graf_data->choose_race_box_top + gBig_font->glyph_y,
  163.         3);
  164.     PDScreenBufferSwap(0);
  165. }
  166.  
  167. // IDA: void __usercall MoveRaceList(int pFrom@<EAX>, int pTo@<EDX>, tS32 pTime_to_move@<EBX>)
  168. void MoveRaceList(int pFrom, int pTo, tS32 pTime_to_move) {
  169.     tS32 start_time;
  170.     tS32 the_time;
  171.     //int move_distance; // Pierre-Marie Baty -- unused variable
  172.     int pitch;
  173.     LOG_TRACE("(%d, %d, %d)", pFrom, pTo, pTime_to_move);
  174.  
  175.     pitch = gCurrent_graf_data->choose_race_y_pitch;
  176.     start_time = PDGetTotalTime();
  177.     while (1) {
  178.         the_time = PDGetTotalTime();
  179.         if (start_time + pTime_to_move <= the_time)
  180.             break;
  181.         DrawRaceList((the_time - start_time) * (pTo - pFrom) * pitch / pTime_to_move + pitch * pFrom);
  182.     }
  183.     DrawRaceList(pitch * pTo);
  184. }
  185.  
  186. // IDA: int __usercall UpRace@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  187. int UpRace(int* pCurrent_choice, int* pCurrent_mode) {
  188.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  189.  
  190.     AddToFlicQueue(gStart_interface_spec->pushed_flics[2].flic_index,
  191.         gStart_interface_spec->pushed_flics[2].x[gGraf_data_index],
  192.         gStart_interface_spec->pushed_flics[2].y[gGraf_data_index],
  193.         1);
  194.     DRS3StartSound(gEffects_outlet, 3000);
  195.     if (gCurrent_race_index != 0 && (gRace_list[gCurrent_race_index - 1].best_rank <= gProgram_state.rank || gProgram_state.game_completed || gChange_race_net_mode)) {
  196.         RemoveTransientBitmaps(1);
  197.         MoveRaceList(gCurrent_race_index, gCurrent_race_index - 1, 150);
  198.         gCurrent_race_index--;
  199.     }
  200.     return 0;
  201. }
  202.  
  203. // IDA: int __usercall DownRace@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  204. int DownRace(int* pCurrent_choice, int* pCurrent_mode) {
  205.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  206.  
  207.     AddToFlicQueue(gStart_interface_spec->pushed_flics[3].flic_index,
  208.         gStart_interface_spec->pushed_flics[3].x[gGraf_data_index],
  209.         gStart_interface_spec->pushed_flics[3].y[gGraf_data_index],
  210.         1);
  211.     DRS3StartSound(gEffects_outlet, 3000);
  212.     if (gCurrent_race_index < gNumber_of_races - 1 && (gProgram_state.rank <= gRace_list[gCurrent_race_index + 1].rank_required || gProgram_state.game_completed || gChange_race_net_mode)) {
  213.         RemoveTransientBitmaps(1);
  214.         MoveRaceList(gCurrent_race_index, gCurrent_race_index + 1, 150);
  215.         gCurrent_race_index++;
  216.     }
  217.     return 0;
  218. }
  219.  
  220. // IDA: int __usercall ClickOnRace@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  221. int ClickOnRace(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  222.     int x_coord;
  223.     int y_coord;
  224.     int race_delta;
  225.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  226.  
  227.     GetMousePosition(&x_coord, &y_coord);
  228.     race_delta = (y_coord - (gCurrent_graf_data->choose_race_curr_y - 5 * (gCurrent_graf_data->choose_race_y_pitch / 2) + gBig_font->glyph_y / 2)) / gCurrent_graf_data->choose_race_y_pitch - 2;
  229.     if (race_delta >= -2 && race_delta <= -1) {
  230.         do {
  231.             UpRace(pCurrent_choice, pCurrent_mode);
  232.             race_delta++;
  233.         } while (race_delta != 0);
  234.     } else if (race_delta > 0 && race_delta < 3) {
  235.         do {
  236.             DownRace(pCurrent_choice, pCurrent_mode);
  237.             race_delta--;
  238.         } while (race_delta != 0);
  239.     }
  240.     return 0;
  241. }
  242.  
  243. // IDA: int __usercall UpClickRace@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  244. int UpClickRace(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  245.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  246.  
  247.     UpRace(pCurrent_choice, pCurrent_mode);
  248.     return 0;
  249. }
  250.  
  251. // IDA: int __usercall DownClickRace@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  252. int DownClickRace(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  253.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  254.  
  255.     DownRace(pCurrent_choice, pCurrent_mode);
  256.     return 0;
  257. }
  258.  
  259. // IDA: void __cdecl StartChangeRace()
  260. void StartChangeRace(void) {
  261.     LOG_TRACE("()");
  262.  
  263.     MoveRaceList(-3, gCurrent_race_index, 400);
  264. }
  265.  
  266. // IDA: int __usercall ChangeRace@<EAX>(int *pRace_index@<EAX>, int pNet_mode@<EDX>, tNet_sequence_type pNet_race_sequence@<EBX>)
  267. int ChangeRace(int* pRace_index, int pNet_mode, tNet_sequence_type pNet_race_sequence) {
  268.     static tFlicette flicker_on[4] = {
  269.         { 43, { 60, 120 }, { 154, 370 } },
  270.         { 43, { 221, 442 }, { 154, 370 } },
  271.         { 221, { 30, 60 }, { 78, 187 } },
  272.         { 221, { 30, 60 }, { 78, 187 } },
  273.     };
  274.     static tFlicette flicker_off[4] = {
  275.         { 42, { 60, 120 }, { 154, 370 } },
  276.         { 42, { 221, 442 }, { 154, 370 } },
  277.         { 220, { 30, 60 }, { 78, 187 } },
  278.         { 220, { 30, 60 }, { 78, 187 } },
  279.     };
  280.     static tFlicette push[4] = {
  281.         { 154, { 60, 120 }, { 154, 370 } },
  282.         { 45, { 221, 442 }, { 154, 370 } },
  283.         { 222, { 30, 60 }, { 78, 187 } },
  284.         { 225, { 30, 60 }, { 118, 283 } },
  285.     };
  286.     static tMouse_area mouse_areas[5] = {
  287.         { { 60, 120 }, { 154, 370 }, { 125, 250 }, { 174, 418 }, 0, 0, 0, NULL },
  288.         { { 221, 442 }, { 154, 370 }, { 286, 572 }, { 174, 418 }, 1, 0, 0, NULL },
  289.         { { 30, 60 }, { 78, 187 }, { 45, 90 }, { 104, 250 }, -1, 0, 0, UpClickRace },
  290.         { { 30, 60 }, { 118, 283 }, { 45, 90 }, { 145, 348 }, -1, 0, 0, DownClickRace },
  291.         { { 66, 132 }, { 33, 79 }, { 278, 556 }, { 144, 346 }, -1, 0, 0, ClickOnRace },
  292.     };
  293.     static tInterface_spec interface_spec = {
  294.         0, 230, 60, 231, 231, 231, 6,
  295.         { -1, 0 }, { -1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
  296.         { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
  297.         { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { UpRace, NULL },
  298.         { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { DownRace, NULL },
  299.         { 1, 1 }, { NULL, NULL }, { 1, 1 }, { NULL, NULL },
  300.         NULL, NULL, 0, NULL, StartChangeRace, NULL, 0, { 0, 0 },
  301.         NULL, 1, 1, COUNT_OF(flicker_on), flicker_on, flicker_off, push,
  302.         COUNT_OF(mouse_areas), mouse_areas, 0, NULL
  303.     };
  304.     int result;
  305.     LOG_TRACE("(%p, %d, %d)", pRace_index, pNet_mode, pNet_race_sequence);
  306.  
  307.     gNet_race_sequence__racestrt = pNet_race_sequence;
  308.     gChange_race_net_mode = pNet_mode;
  309.     gStart_interface_spec = &interface_spec;
  310.     gCurrent_race_index = *pRace_index;
  311.     if (pNet_mode == 0) {
  312.         gBullet_image = LoadPixelmap("BULLET.PIX");
  313.     } else {
  314.         gBullet_image = NULL;
  315.     }
  316.     result = DoInterfaceScreen(&interface_spec, pNet_mode != 0, 0);
  317.     if (result == 0) {
  318.         *pRace_index = gCurrent_race_index;
  319.     }
  320.     if (gBullet_image != NULL) {
  321.         BrPixelmapFree(gBullet_image);
  322.     }
  323.     return result == 0;
  324. }
  325.  
  326. // IDA: void __cdecl DoChangeRace()
  327. void DoChangeRace(void) {
  328.     LOG_TRACE("()");
  329.  
  330.     if (ChangeRace(&gProgram_state.current_race_index, 0, eNet_sequence_sequential) != 0) {
  331.         gProgram_state.current_race_index = gCurrent_race_index;
  332.     }
  333. }
  334.  
  335. // IDA: void __usercall DrawCar(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
  336. void DrawCar(int pCurrent_choice, int pCurrent_mode) {
  337.     char s[64];
  338.     int text_x;
  339.     int text_width;
  340.     LOG_TRACE("(%d, %d)", pCurrent_choice, pCurrent_mode);
  341.  
  342. // Added by dethrace to ignore warnings about using sprintf without a literal format string
  343. #ifndef _MSC_VER // Pierre-Marie Baty -- exclude MSVC
  344. #pragma GCC diagnostic push
  345. #pragma GCC diagnostic ignored "-Wformat-security"
  346. #endif // !_MSC_VER
  347.  
  348.     PollCarDetails(gChoose_car_net_game);
  349.     if (gChange_race_net_mode == 0) {
  350.         if (gProgram_state.number_of_cars == 1) {
  351.             sprintf(s, GetMiscString(kMiscString_NoOtherCarsToChooseFromYet));
  352.         } else if (gProgram_state.cars_available[gCurrent_car_index] == gProgram_state.frank_or_anniness) {
  353.             sprintf(s, GetMiscString(kMiscString_YourOriginalCar));
  354.         } else {
  355.             sprintf(s, "%s %s", GetMiscString(kMiscString_OriginalDriverWas), gOpponents[gProgram_state.cars_available[gCurrent_car_index]].name);
  356.         }
  357.     } else if (gCar_details[gProgram_state.cars_available[gCurrent_car_index]].ownership == eCar_owner_someone) {
  358.         sprintf(s, "%s %s", GetMiscString(kMiscString_THIS_CAR_ALREADY_TAKEN_BY), gCar_details[gProgram_state.cars_available[gCurrent_car_index]].name);
  359.     } else {
  360.         sprintf(s, GetMiscString(kMiscString_AVAILABLE));
  361.     }
  362.  
  363. #ifndef _MSC_VER // Pierre-Marie Baty -- exclude MSVC
  364. #pragma GCC diagnostic pop
  365. #endif // !_MSC_VER
  366.  
  367.     text_width = BrPixelmapTextWidth(gBack_screen, gFont_7, s);
  368.     text_x = (gCurrent_graf_data->change_car_line_right + gCurrent_graf_data->change_car_line_left - text_width) / 2;
  369.     BrPixelmapRectangleFill(gBack_screen,
  370.         gCurrent_graf_data->change_car_line_left,
  371.         gCurrent_graf_data->change_car_text_y,
  372.         gCurrent_graf_data->change_car_line_right - gCurrent_graf_data->change_car_line_left,
  373.         gFont_7->glyph_y,
  374.         0);
  375.     TransBrPixelmapText(gBack_screen,
  376.         text_x,
  377.         gCurrent_graf_data->change_car_text_y,
  378.         3,
  379.         gFont_7,
  380.         s);
  381.     BrPixelmapLine(gBack_screen,
  382.         gCurrent_graf_data->change_car_line_left,
  383.         gCurrent_graf_data->change_car_line_y,
  384.         gCurrent_graf_data->change_car_line_right,
  385.         gCurrent_graf_data->change_car_line_y,
  386.         6);
  387.     if (gChange_race_net_mode && gCar_details[gProgram_state.cars_available[gCurrent_car_index]].ownership == eCar_owner_someone) {
  388.         DRPixelmapRectangleMaskedCopy(
  389.             gBack_screen,
  390.             (gCurrent_graf_data->change_car_panel_left + gCurrent_graf_data->change_car_panel_right - gTaken_image->width) / 2,
  391.             (gCurrent_graf_data->change_car_panel_bottom + gCurrent_graf_data->change_car_panel_bottom - gTaken_image->height) / 2,
  392.             gTaken_image,
  393.             0,
  394.             0,
  395.             gTaken_image->width,
  396.             gTaken_image->height);
  397.     }
  398. }
  399.  
  400. // IDA: void __cdecl SetCarFlic()
  401. void SetCarFlic(void) {
  402.     LOG_TRACE("()");
  403.  
  404.     ChangePanelFlic(0,
  405.         gOpponents[gProgram_state.cars_available[gCurrent_car_index]].stolen_car_image_data,
  406.         gOpponents[gProgram_state.cars_available[gCurrent_car_index]].stolen_car_image_data_length);
  407. }
  408.  
  409. // IDA: int __usercall UpCar@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  410. int UpCar(int* pCurrent_choice, int* pCurrent_mode) {
  411.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  412.  
  413.     AddToFlicQueue(gStart_interface_spec->pushed_flics[2].flic_index,
  414.         gStart_interface_spec->pushed_flics[2].x[gGraf_data_index],
  415.         gStart_interface_spec->pushed_flics[2].y[gGraf_data_index],
  416.         1);
  417.     DRS3StartSound(gEffects_outlet, 3000);
  418.     if (gCurrent_car_index != 0) {
  419.         RemoveTransientBitmaps(1);
  420.         DropOutImageThruBottom(GetPanelPixelmap(0),
  421.             gCurrent_graf_data->change_car_panel_left,
  422.             gCurrent_graf_data->change_car_panel_top,
  423.             gCurrent_graf_data->change_car_panel_top_clip,
  424.             gCurrent_graf_data->change_car_panel_bottom_clip);
  425.         gCurrent_car_index--;
  426.         SetCarFlic();
  427.         DropInImageFromTop(GetPanelPixelmap(0),
  428.             gCurrent_graf_data->change_car_panel_left,
  429.             gCurrent_graf_data->change_car_panel_top,
  430.             gCurrent_graf_data->change_car_panel_top_clip,
  431.             gCurrent_graf_data->change_car_panel_bottom_clip);
  432.     }
  433.     return 0;
  434. }
  435.  
  436. // IDA: int __usercall DownCar@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  437. int DownCar(int* pCurrent_choice, int* pCurrent_mode) {
  438.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  439.  
  440.     AddToFlicQueue(gStart_interface_spec->pushed_flics[3].flic_index,
  441.         gStart_interface_spec->pushed_flics[3].x[gGraf_data_index],
  442.         gStart_interface_spec->pushed_flics[3].y[gGraf_data_index],
  443.         1);
  444.     DRS3StartSound(gEffects_outlet, 3000);
  445.     if (gCurrent_car_index < gProgram_state.number_of_cars - 1) {
  446.         RemoveTransientBitmaps(1);
  447.         DropOutImageThruTop(GetPanelPixelmap(0),
  448.             gCurrent_graf_data->change_car_panel_left,
  449.             gCurrent_graf_data->change_car_panel_top,
  450.             gCurrent_graf_data->change_car_panel_top_clip,
  451.             gCurrent_graf_data->change_car_panel_bottom_clip);
  452.         gCurrent_car_index++;
  453.         SetCarFlic();
  454.         DropInImageFromBottom(GetPanelPixelmap(0),
  455.             gCurrent_graf_data->change_car_panel_left,
  456.             gCurrent_graf_data->change_car_panel_top,
  457.             gCurrent_graf_data->change_car_panel_top_clip,
  458.             gCurrent_graf_data->change_car_panel_bottom_clip);
  459.     }
  460.     return 0;
  461. }
  462.  
  463. // IDA: int __usercall UpClickCar@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  464. int UpClickCar(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  465.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  466.  
  467.     UpCar(pCurrent_choice, pCurrent_mode);
  468.     return 0;
  469. }
  470.  
  471. // IDA: int __usercall DownClickCar@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  472. int DownClickCar(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  473.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  474.  
  475.     DownCar(pCurrent_choice, pCurrent_mode);
  476.     return 0;
  477. }
  478.  
  479. // IDA: int __usercall ChangeCarGoAhead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  480. int ChangeCarGoAhead(int* pCurrent_choice, int* pCurrent_mode) {
  481.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  482.  
  483.     if (gChange_race_net_mode == 0 || gCar_details[gProgram_state.cars_available[gCurrent_car_index]].ownership != eCar_owner_someone) {
  484.         return 1;
  485.     } else {
  486.         DRS3StartSound(gEffects_outlet, 3100);
  487.         return 0;
  488.     }
  489. }
  490.  
  491. // IDA: int __usercall ChangeCar@<EAX>(int pNet_mode@<EAX>, int *pCar_index@<EDX>, tNet_game_details *pNet_game@<EBX>)
  492. int ChangeCar(int pNet_mode, int* pCar_index, tNet_game_details* pNet_game) {
  493.     static tFlicette flicker_on[4] = {
  494.         { 43, { 60, 120 }, { 154, 370 } },
  495.         { 43, { 221, 442 }, { 154, 370 } },
  496.         { 221, { 30, 60 }, { 78, 187 } },
  497.         { 221, { 30, 60 }, { 78, 187 } },
  498.     };
  499.     static tFlicette flicker_off[4] = {
  500.         { 42, { 60, 120 }, { 154, 370 } },
  501.         { 42, { 221, 442 }, { 154, 370 } },
  502.         { 220, { 30, 60 }, { 78, 187 } },
  503.         { 220, { 30, 60 }, { 78, 187 } },
  504.     };
  505.     static tFlicette push[4] = {
  506.         { 154, { 60, 120 }, { 154, 370 } },
  507.         { 45, { 221, 442 }, { 154, 370 } },
  508.         { 222, { 30, 60 }, { 78, 187 } },
  509.         { 225, { 30, 60 }, { 118, 283 } },
  510.     };
  511.     static tMouse_area mouse_areas[4] = {
  512.         { { 60, 120 }, { 154, 370 }, { 125, 250 }, { 174, 418 }, 0, 0, 0, NULL },
  513.         { { 221, 442 }, { 154, 370 }, { 286, 572 }, { 174, 418 }, 1, 0, 0, NULL },
  514.         { { 30, 60 }, { 78, 187 }, { 45, 90 }, { 104, 250 }, -1, 0, 0, UpClickCar },
  515.         { { 30, 60 }, { 118, 283 }, { 45, 90 }, { 145, 348 }, -1, 0, 0, DownClickCar },
  516.     };
  517.     static tInterface_spec interface_spec = {
  518.         0,
  519.         236,
  520.         62,
  521.         0,
  522.         0,
  523.         0,
  524.         -1,
  525.         { -1, 0 },
  526.         { -1, 0 },
  527.         { 0, 0 },
  528.         { 1, 0 },
  529.         { NULL, NULL },
  530.         { -1, 0 },
  531.         { 1, 0 },
  532.         { 0, 0 },
  533.         { 1, 0 },
  534.         { NULL, NULL },
  535.         { -1, 0 },
  536.         { 0, 0 },
  537.         { 0, 0 },
  538.         { 0, 0 },
  539.         { UpCar, NULL },
  540.         { -1, 0 },
  541.         { 0, 0 },
  542.         { 0, 0 },
  543.         { 0, 0 },
  544.         { DownCar, NULL },
  545.         { 1, 1 },
  546.         { ChangeCarGoAhead, NULL },
  547.         { 1, 1 },
  548.         { NULL, NULL },
  549.         NULL,
  550.         DrawCar,
  551.         0,
  552.         NULL,
  553.         NULL,
  554.         NULL,
  555.         0,
  556.         { 0, 0 },
  557.         NULL,
  558.         1,
  559.         1,
  560.         COUNT_OF(flicker_on),
  561.         flicker_on,
  562.         flicker_off,
  563.         push,
  564.         COUNT_OF(mouse_areas),
  565.         mouse_areas,
  566.         0,
  567.         NULL,
  568.     };
  569.     int i;
  570.     int result;
  571.     int power_up_levels[3];
  572.     LOG_TRACE("(%d, %p, %p)", pNet_mode, pCar_index, pNet_game);
  573.  
  574.     gChoose_car_net_game = pNet_game;
  575.     gChange_race_net_mode = pNet_mode;
  576.     gStart_interface_spec = &interface_spec;
  577.     for (i = 0; i < gProgram_state.number_of_cars; i++) {
  578.         if (gProgram_state.cars_available[i] == *pCar_index) {
  579.             gCurrent_car_index = i;
  580.             break;
  581.         }
  582.     }
  583.     if (gProgram_state.game_completed) {
  584.         gProgram_state.number_of_cars = gNumber_of_racers;
  585.         for (i = 0; i < gProgram_state.number_of_cars; i++) {
  586.             gProgram_state.cars_available[i] = i;
  587.         }
  588.     }
  589.     for (i = 0; i < gProgram_state.number_of_cars; i++) {
  590.         if (gOpponents[gProgram_state.cars_available[i]].stolen_car_image_data == NULL) {
  591.             LoadFlicData(gOpponents[gProgram_state.cars_available[i]].stolen_car_flic_name,
  592.                 &gOpponents[gProgram_state.cars_available[i]].stolen_car_image_data,
  593.                 &gOpponents[gProgram_state.cars_available[i]].stolen_car_image_data_length);
  594.         } else {
  595.             MAMSLock((void**)&gOpponents[gProgram_state.cars_available[i]].stolen_car_image_data);
  596.         }
  597.     }
  598.     InitialiseFlicPanel(0,
  599.         gCurrent_graf_data->change_car_panel_left,
  600.         gCurrent_graf_data->change_car_panel_top,
  601.         gCurrent_graf_data->change_car_panel_right - gCurrent_graf_data->change_car_panel_left,
  602.         gCurrent_graf_data->change_car_panel_bottom - gCurrent_graf_data->change_car_panel_top);
  603.     SetCarFlic();
  604.     if (pNet_mode) {
  605.         gTaken_image = LoadPixelmap("TAKEN.PIX");
  606.     }
  607.     result = DoInterfaceScreen(&interface_spec, pNet_mode, 0);
  608.     if (pNet_mode) {
  609.         FadePaletteDown();
  610.     } else {
  611.         RunFlic(237);
  612.     }
  613.     for (i = 0; i < gProgram_state.number_of_cars; i++) {
  614.         MAMSUnlock((void**)&gOpponents[gProgram_state.cars_available[i]].stolen_car_image_data);
  615.     }
  616.     DisposeFlicPanel(0);
  617.     if (pNet_mode) {
  618.         BrPixelmapFree(gTaken_image);
  619.     }
  620.     if (result == 0) {
  621.         if (pNet_mode) {
  622.             *pCar_index = gProgram_state.cars_available[gCurrent_race_index];
  623.         } else {
  624.             AboutToLoadFirstCar();
  625.             SwitchToRealResolution();
  626.             for (i = 0; i < COUNT_OF(power_up_levels); i++) {
  627.                 power_up_levels[i] = gProgram_state.current_car.power_up_levels[i];
  628.             }
  629.             LoadCar(gOpponents[gProgram_state.cars_available[gCurrent_car_index]].car_file_name,
  630.                 eDriver_local_human,
  631.                 &gProgram_state.current_car,
  632.                 gProgram_state.cars_available[gCurrent_car_index],
  633.                 gProgram_state.player_name[gProgram_state.frank_or_anniness],
  634.                 &gOur_car_storage_space);
  635.             for (i = 0; i < COUNT_OF(power_up_levels); i++) {
  636.                 gProgram_state.current_car.power_up_levels[i] = power_up_levels[i];
  637.             }
  638.             SwitchToLoresMode();
  639.             SetCarStorageTexturingLevel(&gOur_car_storage_space, GetCarTexturingLevel(), eCTL_full);
  640.             DisposeRaceInfo(&gCurrent_race);
  641.             SelectOpponents(&gCurrent_race);
  642.             LoadRaceInfo(gProgram_state.current_race_index, &gCurrent_race);
  643.         }
  644.         return 1;
  645.     } else {
  646.         return 0;
  647.     }
  648. }
  649.  
  650. // IDA: void __cdecl DoChangeCar()
  651. void DoChangeCar(void) {
  652.     LOG_TRACE("()");
  653.  
  654.     ChangeCar(0, &gProgram_state.current_car.index, NULL);
  655. }
  656.  
  657. // IDA: int __cdecl PartsShopRecommended()
  658. int PartsShopRecommended(void) {
  659.     int running_cost;
  660.     int i;
  661.     int current_index;
  662.     int counter;
  663.     LOG_TRACE("()");
  664.  
  665.     running_cost = 0;
  666.     counter = 0;
  667.     for (i = 0; i < eParts_count; i++) {
  668.         current_index = gProgram_state.current_car.power_up_levels[i];
  669.         if (current_index + 1 < gProgram_state.current_car.power_ups[i].number_of_parts && (gProgram_state.rank <= gProgram_state.current_car.power_ups[i].info[current_index + 1].rank_required || gProgram_state.game_completed)) {
  670.             running_cost += gProgram_state.current_car.power_ups[i].info[current_index + 1].prices[gProgram_state.skill_level];
  671.             counter++;
  672.         }
  673.     }
  674.     return running_cost != 0 && ((float)running_cost / counter * 1.5f <= gProgram_state.credits);
  675. }
  676.  
  677. // IDA: void __usercall CalcPartPrice(int pCategory@<EAX>, int pIndex@<EDX>, int *pPrice@<EBX>, int *pCost@<ECX>)
  678. void CalcPartPrice(int pCategory, int pIndex, int* pPrice, int* pCost) {
  679.     //int current_value; // Pierre-Marie Baty -- unused variable
  680.     LOG_TRACE("(%d, %d, %p, %p)", pCategory, pIndex, pPrice, pCost);
  681.  
  682.     *pPrice = gProgram_state.current_car.power_ups[pCategory].info[pIndex].prices[gProgram_state.skill_level];
  683.     if (gProgram_state.current_car.power_up_levels[pCategory] == pIndex) {
  684.         *pCost = 0;
  685.     } else {
  686.         *pCost = *pPrice - 10 * (gRefund_rate * gProgram_state.current_car.power_ups[pCategory].info[gProgram_state.current_car.power_up_levels[pCategory]].prices[gProgram_state.skill_level] / 1000);
  687.     }
  688. }
  689.  
  690. // IDA: int __usercall BuyPart@<EAX>(int pCategory@<EAX>, int pIndex@<EDX>)
  691. int BuyPart(int pCategory, int pIndex) {
  692.     int price;
  693.     int cost;
  694.     LOG_TRACE("(%d, %d)", pCategory, pIndex);
  695.  
  696.     CalcPartPrice(pCategory, pIndex, &price, &cost);
  697.     if (cost == 0) {
  698.         return 1;
  699.     } else if (gProgram_state.credits < cost) {
  700.         return 0;
  701.     } else {
  702.         gProgram_state.credits -= cost;
  703.         if (gProgram_state.credits > 999999) {
  704.             gProgram_state.credits = 999999;
  705.         }
  706.         gProgram_state.current_car.power_up_levels[pCategory] = pIndex;
  707.         return 1;
  708.     }
  709. }
  710.  
  711. // IDA: void __cdecl DoAutoParts()
  712. void DoAutoParts(void) {
  713.     int i;
  714.     int lowest_yet;
  715.     int lowest_one;
  716.     int price;
  717.     int cost;
  718.     int current_level;
  719.     LOG_TRACE("()");
  720.  
  721.     while (1) {
  722.         if (!PartsShopRecommended()) {
  723.             return;
  724.         }
  725.         lowest_yet = COUNT_OF(((tParts_spec*)NULL)->info);
  726.         for (i = 0; i < eParts_count; i++) {
  727.             current_level = gProgram_state.current_car.power_up_levels[i];
  728.             if (current_level + 1 < gProgram_state.current_car.power_ups[i].number_of_parts && (gProgram_state.rank <= gProgram_state.current_car.power_ups[i].info[current_level + 1].rank_required || gProgram_state.game_completed)) {
  729.                 CalcPartPrice(i, current_level + 1, &price, &cost);
  730.                 if (cost != 0 && cost <= gProgram_state.credits && current_level < lowest_yet) {
  731.                     lowest_one = i;
  732.                     lowest_yet = current_level + 1;
  733.                 }
  734.             }
  735.         }
  736.         if (lowest_yet == COUNT_OF(((tParts_spec*)NULL)->info)) {
  737.             break;
  738.         }
  739.         if (!BuyPart(lowest_one, lowest_yet)) {
  740.             return;
  741.         }
  742.     }
  743. }
  744.  
  745. // IDA: void __cdecl DrawPartsLabel()
  746. void DrawPartsLabel(void) {
  747.     LOG_TRACE("()");
  748.  
  749.     switch (gPart_category) {
  750.     case eParts_armour:
  751.         RunFlicAt(262, gCurrent_graf_data->parts_label_x, gCurrent_graf_data->parts_label_y);
  752.         break;
  753.     case eParts_power:
  754.         RunFlicAt(263, gCurrent_graf_data->parts_label_x, gCurrent_graf_data->parts_label_y);
  755.         break;
  756.     case eParts_offensive:
  757.         RunFlicAt(264, gCurrent_graf_data->parts_label_x, gCurrent_graf_data->parts_label_y);
  758.         break;
  759.     default:
  760.         break;
  761.     }
  762. }
  763.  
  764. // IDA: void __usercall ErasePartsText(int pTotal_as_well@<EAX>)
  765. void ErasePartsText(int pTotal_as_well) {
  766.     LOG_TRACE("(%d)", pTotal_as_well);
  767.  
  768.     BrPixelmapRectangleFill(gBack_screen,
  769.         gCurrent_graf_data->parts_cost_x,
  770.         gCurrent_graf_data->parts_cost_y,
  771.         gCurrent_graf_data->parts_image_width - (gCurrent_graf_data->parts_cost_x - gCurrent_graf_data->parts_image_x),
  772.         gFont_7->glyph_y + gCurrent_graf_data->parts_net_y - gCurrent_graf_data->parts_cost_y,
  773.         0);
  774.     if (pTotal_as_well) {
  775.         BrPixelmapRectangleFill(gBack_screen,
  776.             gCurrent_graf_data->parts_total_x,
  777.             gCurrent_graf_data->parts_total_y,
  778.             gCurrent_graf_data->parts_image_width - (gCurrent_graf_data->parts_total_x - gCurrent_graf_data->parts_image_x),
  779.             gFont_7->glyph_y,
  780.             0);
  781.     }
  782. }
  783.  
  784. // IDA: void __cdecl DrawPartsText()
  785. void DrawPartsText(void) {
  786.     int price;
  787.     int cost;
  788.     LOG_TRACE("()");
  789.  
  790.     CalcPartPrice(gPart_category, gPart_index, &price, &cost);
  791.     TransBrPixelmapText(gBack_screen, gCurrent_graf_data->parts_cost_x, gCurrent_graf_data->parts_cost_y, 5, gFont_7, GetMiscString(kMiscString_RetailColon));
  792.     BrPixelmapTextF(gBack_screen, gCurrent_graf_data->parts_numbers_x, gCurrent_graf_data->parts_cost_y - (TranslationMode() ? 2 : 0), 5, gFont_7, "%d", price);
  793.     if (cost > 0) {
  794.         TransBrPixelmapText(gBack_screen, gCurrent_graf_data->parts_net_x, gCurrent_graf_data->parts_net_y, 45, gFont_7, GetMiscString(kMiscString_YouPayColon));
  795.         BrPixelmapTextF(gBack_screen, gCurrent_graf_data->parts_numbers_x, gCurrent_graf_data->parts_net_y - (TranslationMode() ? 2 : 0), 45, gFont_7, "%d", cost);
  796.     } else if (cost < 0) {
  797.         TransBrPixelmapText(gBack_screen, gCurrent_graf_data->parts_net_x, gCurrent_graf_data->parts_net_y, 201, gFont_7, GetMiscString(kMiscString_RefundColon));
  798.         BrPixelmapTextF(gBack_screen, gCurrent_graf_data->parts_numbers_x, gCurrent_graf_data->parts_net_y - (TranslationMode() ? 2 : 0), 201, gFont_7, "%d", -cost);
  799.     } else if (gJust_bought_part) {
  800.         TransBrPixelmapText(gBack_screen, gCurrent_graf_data->parts_net_x, gCurrent_graf_data->parts_net_y, 134, gFont_7, GetMiscString(kMiscString_YouGotIt));
  801.     } else {
  802.         TransBrPixelmapText(gBack_screen, gCurrent_graf_data->parts_net_x, gCurrent_graf_data->parts_net_y, 134, gFont_7, GetMiscString(kMiscString_YoursAlready));
  803.     }
  804.     TransBrPixelmapText(gBack_screen, gCurrent_graf_data->parts_total_x, gCurrent_graf_data->parts_total_y, 2, gFont_7, GetMiscString(kMiscString_CreditsColon));
  805.     BrPixelmapTextF(gBack_screen, gCurrent_graf_data->parts_numbers_x, gCurrent_graf_data->parts_total_y - (TranslationMode() ? 2 : 0), 2, gFont_7, "%d", gProgram_state.credits);
  806. }
  807.  
  808. // IDA: void __cdecl SetPartsImage()
  809. void SetPartsImage(void) {
  810.     LOG_TRACE("()");
  811.  
  812.     ChangePanelFlic(0,
  813.         gProgram_state.current_car.power_ups[gPart_category].info[gPart_index].data_ptr,
  814.         gProgram_state.current_car.power_ups[gPart_category].info[gPart_index].data_length);
  815.     TellyInImage(GetPanelPixelmap(0), gCurrent_graf_data->parts_image_x, gCurrent_graf_data->parts_image_y);
  816.     DrawPartsText();
  817. }
  818.  
  819. // IDA: int __cdecl GetPartsMax()
  820. int GetPartsMax(void) {
  821.     int i;
  822.     LOG_TRACE("()");
  823.  
  824.     for (i = gProgram_state.current_car.power_ups[gPart_category].number_of_parts - 1; i >= 0; i--) {
  825.         if (gProgram_state.rank <= gProgram_state.current_car.power_ups[gPart_category].info[i].rank_required) {
  826.             return i;
  827.         }
  828.         if (gProgram_state.game_completed) {
  829.             return i;
  830.         }
  831.     }
  832.     return 0;
  833. }
  834.  
  835. // IDA: void __cdecl CalcPartsIndex()
  836. void CalcPartsIndex(void) {
  837.     //int current_index; // Pierre-Marie Baty -- unused variable
  838.     LOG_TRACE("()");
  839.  
  840.     gPart_index = gProgram_state.current_car.power_up_levels[gPart_category];
  841.     if (gPart_index + 1 < gProgram_state.current_car.power_ups[gPart_category].number_of_parts && (gProgram_state.rank <= gProgram_state.current_car.power_ups[gPart_category].info[gPart_index + 1].rank_required || gProgram_state.game_completed)) {
  842.         gPart_index += 1;
  843.     }
  844. }
  845.  
  846. // IDA: void __cdecl DoExchangePart()
  847. void DoExchangePart(void) {
  848.     int price;
  849.     int cost;
  850.     LOG_TRACE("()");
  851.  
  852.     CalcPartPrice(gPart_category, gPart_index, &price, &cost);
  853.     if (cost == 0 || gProgram_state.credits < cost) {
  854.         DRS3StartSound(gEffects_outlet, 3100);
  855.     } else {
  856.         gJust_bought_part = 1;
  857.         DRS3StartSound(gEffects_outlet, 3101);
  858.         BuyPart(gPart_category, gPart_index);
  859.         ErasePartsText(1);
  860.         DrawPartsText();
  861.     }
  862. }
  863.  
  864. // IDA: int __usercall PartsShopGoAhead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  865. int PartsShopGoAhead(int* pCurrent_choice, int* pCurrent_mode) {
  866.     //int flic_index; // Pierre-Marie Baty -- unused variable
  867.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  868.  
  869.     if (*pCurrent_choice < 3 && *pCurrent_mode == 0) {
  870.         RemoveTransientBitmaps(1);
  871.         DontLetFlicFuckWithPalettes();
  872.         TurnFlicTransparencyOn();
  873.         RunFlicAt(
  874.             gStart_interface_spec->pushed_flics[*pCurrent_choice].flic_index,
  875.             gStart_interface_spec->pushed_flics[*pCurrent_choice].x[gGraf_data_index],
  876.             gStart_interface_spec->pushed_flics[*pCurrent_choice].y[gGraf_data_index]);
  877.         TurnFlicTransparencyOff();
  878.         LetFlicFuckWithPalettes();
  879.         gJust_bought_part = 0;
  880.         ErasePartsText(1);
  881.         TellyOutImage(GetPanelPixelmap(0), gCurrent_graf_data->parts_image_x, gCurrent_graf_data->parts_image_y);
  882.         gPart_category = *pCurrent_choice;
  883.         CalcPartsIndex();
  884.         RunFlic(261);
  885.         AddToFlicQueue(
  886.             gStart_interface_spec->flicker_on_flics[*pCurrent_choice].flic_index,
  887.             gStart_interface_spec->flicker_on_flics[*pCurrent_choice].x[gGraf_data_index],
  888.             gStart_interface_spec->flicker_on_flics[*pCurrent_choice].y[gGraf_data_index],
  889.             1);
  890.         DrawPartsLabel();
  891.         SetPartsImage();
  892.         ProcessFlicQueue(gFrame_period);
  893.         DoMouseCursor();
  894.         PDScreenBufferSwap(0);
  895.         return 0;
  896.     } else if (*pCurrent_mode == 1) {
  897.         AddToFlicQueue(
  898.             gStart_interface_spec->flicker_on_flics[4].flic_index,
  899.             gStart_interface_spec->flicker_on_flics[4].x[gGraf_data_index],
  900.             gStart_interface_spec->flicker_on_flics[4].y[gGraf_data_index],
  901.             1);
  902.         DoExchangePart();
  903.         return 0;
  904.     } else {
  905.         return 1;
  906.     }
  907. }
  908.  
  909. // IDA: int __usercall UpPart@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  910. int UpPart(int* pCurrent_choice, int* pCurrent_mode) {
  911.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  912.  
  913.     gJust_bought_part = 0;
  914.     AddToFlicQueue(
  915.         gStart_interface_spec->pushed_flics[5].flic_index,
  916.         gStart_interface_spec->pushed_flics[5].x[gGraf_data_index],
  917.         gStart_interface_spec->pushed_flics[5].y[gGraf_data_index],
  918.         1);
  919.     DRS3StartSound(gEffects_outlet, 3000);
  920.     RemoveTransientBitmaps(1);
  921.     ErasePartsText(0);
  922.  
  923.     gPart_index = (gPart_index == 0) ? GetPartsMax() : (gPart_index - 1);
  924.  
  925.     DropOutImageThruBottom(
  926.         GetPanelPixelmap(0),
  927.         gCurrent_graf_data->parts_image_x,
  928.         gCurrent_graf_data->parts_image_y,
  929.         gCurrent_graf_data->parts_top_clip,
  930.         gCurrent_graf_data->parts_bottom_clip);
  931.     ChangePanelFlic(0,
  932.         gProgram_state.current_car.power_ups[gPart_category].info[gPart_index].data_ptr,
  933.         gProgram_state.current_car.power_ups[gPart_category].info[gPart_index].data_length);
  934.     DropInImageFromTop(
  935.         GetPanelPixelmap(0),
  936.         gCurrent_graf_data->parts_image_x,
  937.         gCurrent_graf_data->parts_image_y,
  938.         gCurrent_graf_data->parts_top_clip,
  939.         gCurrent_graf_data->parts_bottom_clip);
  940.     DrawPartsText();
  941.     return 0;
  942. }
  943.  
  944. // IDA: int __usercall DownPart@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  945. int DownPart(int* pCurrent_choice, int* pCurrent_mode) {
  946.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  947.  
  948.     gJust_bought_part = 0;
  949.     AddToFlicQueue(
  950.         gStart_interface_spec->pushed_flics[6].flic_index,
  951.         gStart_interface_spec->pushed_flics[6].x[gGraf_data_index],
  952.         gStart_interface_spec->pushed_flics[6].y[gGraf_data_index],
  953.         1);
  954.     DRS3StartSound(gEffects_outlet, 3000);
  955.     RemoveTransientBitmaps(1);
  956.     ErasePartsText(0);
  957.  
  958.     gPart_index = (gPart_index == GetPartsMax()) ? 0 : (gPart_index + 1);
  959.  
  960.     DropOutImageThruTop(
  961.         GetPanelPixelmap(0),
  962.         gCurrent_graf_data->parts_image_x,
  963.         gCurrent_graf_data->parts_image_y,
  964.         gCurrent_graf_data->parts_top_clip,
  965.         gCurrent_graf_data->parts_bottom_clip);
  966.     ChangePanelFlic(0,
  967.         gProgram_state.current_car.power_ups[gPart_category].info[gPart_index].data_ptr,
  968.         gProgram_state.current_car.power_ups[gPart_category].info[gPart_index].data_length);
  969.     DropInImageFromBottom(
  970.         GetPanelPixelmap(0),
  971.         gCurrent_graf_data->parts_image_x,
  972.         gCurrent_graf_data->parts_image_y,
  973.         gCurrent_graf_data->parts_top_clip,
  974.         gCurrent_graf_data->parts_bottom_clip);
  975.     DrawPartsText();
  976.     return 0;
  977. }
  978.  
  979. // IDA: int __usercall UpClickPart@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  980. int UpClickPart(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  981.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  982.  
  983.     UpPart(pCurrent_choice, pCurrent_mode);
  984.     return 0;
  985. }
  986.  
  987. // IDA: int __usercall DownClickPart@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  988. int DownClickPart(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  989.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  990.  
  991.     DownPart(pCurrent_choice, pCurrent_mode);
  992.     return 0;
  993. }
  994.  
  995. // IDA: int __usercall PartsArrowsOn@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  996. int PartsArrowsOn(int* pCurrent_choice, int* pCurrent_mode) {
  997.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  998.  
  999.     AddToFlicQueue(
  1000.         gStart_interface_spec->flicker_on_flics[5].flic_index,
  1001.         gStart_interface_spec->flicker_on_flics[5].x[gGraf_data_index],
  1002.         gStart_interface_spec->flicker_on_flics[5].y[gGraf_data_index],
  1003.         1);
  1004.     AddToFlicQueue(gStart_interface_spec->flicker_on_flics[6].flic_index,
  1005.         gStart_interface_spec->flicker_on_flics[6].x[gGraf_data_index],
  1006.         gStart_interface_spec->flicker_on_flics[6].y[gGraf_data_index],
  1007.         1);
  1008.     return 0;
  1009. }
  1010.  
  1011. // IDA: int __usercall PartsArrowsOff@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  1012. int PartsArrowsOff(int* pCurrent_choice, int* pCurrent_mode) {
  1013.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  1014.  
  1015.     AddToFlicQueue(gStart_interface_spec->flicker_off_flics[5].flic_index,
  1016.         gStart_interface_spec->flicker_off_flics[5].x[gGraf_data_index],
  1017.         gStart_interface_spec->flicker_off_flics[5].y[gGraf_data_index],
  1018.         1);
  1019.     AddToFlicQueue(gStart_interface_spec->flicker_off_flics[6].flic_index,
  1020.         gStart_interface_spec->flicker_off_flics[6].x[gGraf_data_index],
  1021.         gStart_interface_spec->flicker_off_flics[6].y[gGraf_data_index],
  1022.         1);
  1023.     return 0;
  1024. }
  1025.  
  1026. // IDA: void __cdecl StartPartsShop()
  1027. void StartPartsShop(void) {
  1028.     LOG_TRACE("()");
  1029.  
  1030.     DrawPartsLabel();
  1031.     SetPartsImage();
  1032. }
  1033.  
  1034. // IDA: int __usercall DonePartsShop@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
  1035. int DonePartsShop(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
  1036.     LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
  1037.  
  1038.     if (gFade_away_parts_shop) {
  1039.         FadePaletteDown();
  1040.     } else {
  1041.         RunFlic(251);
  1042.     }
  1043.     return pGo_ahead;
  1044. }
  1045.  
  1046. // IDA: void __usercall DrawPartsShop(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
  1047. void DrawPartsShop(int pCurrent_choice, int pCurrent_mode) {
  1048.     LOG_TRACE("(%d, %d)", pCurrent_choice, pCurrent_mode);
  1049.  
  1050.     DrawPartsText();
  1051. }
  1052.  
  1053. // IDA: void __usercall DoPartsShop(int pFade_away@<EAX>)
  1054. void DoPartsShop(int pFade_away) {
  1055.     static tFlicette flicker_on[7] = {
  1056.         { 43, { 225, 450 }, { 30, 72 } },
  1057.         { 43, { 225, 450 }, { 60, 144 } },
  1058.         { 43, { 225, 450 }, { 89, 214 } },
  1059.         { 43, { 225, 450 }, { 152, 365 } },
  1060.         { 43, { 85, 170 }, { 152, 365 } },
  1061.         { 221, { 30, 60 }, { 79, 190 } },
  1062.         { 221, { 30, 60 }, { 79, 190 } },
  1063.     };
  1064.     static tFlicette flicker_off[7] = {
  1065.         { 42, { 225, 450 }, { 30, 72 } },
  1066.         { 42, { 225, 450 }, { 60, 144 } },
  1067.         { 42, { 225, 450 }, { 89, 214 } },
  1068.         { 42, { 225, 450 }, { 152, 365 } },
  1069.         { 42, { 85, 170 }, { 152, 365 } },
  1070.         { 220, { 30, 60 }, { 79, 190 } },
  1071.         { 220, { 30, 60 }, { 79, 190 } },
  1072.     };
  1073.     static tFlicette push[7] = {
  1074.         { 254, { 225, 450 }, { 30, 72 } },
  1075.         { 255, { 225, 450 }, { 60, 144 } },
  1076.         { 256, { 225, 450 }, { 89, 214 } },
  1077.         { 154, { 225, 450 }, { 152, 365 } },
  1078.         { 260, { 85, 170 }, { 152, 365 } },
  1079.         { 222, { 30, 60 }, { 79, 190 } },
  1080.         { 225, { 30, 60 }, { 120, 288 } },
  1081.     };
  1082.     static tMouse_area mouse_areas[7] = {
  1083.         { { 225, 450 }, { 30, 72 }, { 288, 576 }, { 50, 120 }, 0, 0, 0, NULL },
  1084.         { { 225, 450 }, { 60, 144 }, { 288, 576 }, { 80, 192 }, 1, 0, 0, NULL },
  1085.         { { 225, 450 }, { 89, 214 }, { 288, 576 }, { 109, 262 }, 2, 0, 0, NULL },
  1086.         { { 225, 450 }, { 152, 365 }, { 288, 576 }, { 172, 413 }, 3, 0, 0, NULL },
  1087.         { { 85, 170 }, { 152, 365 }, { 148, 296 }, { 172, 413 }, 4, 1, 0, NULL },
  1088.         { { 30, 60 }, { 79, 190 }, { 45, 90 }, { 106, 254 }, -1, 1, 0, UpClickPart },
  1089.         { { 30, 60 }, { 120, 288 }, { 45, 90 }, { 147, 353 }, -1, 1, 0, DownClickPart },
  1090.     };
  1091.     static tInterface_spec interface_spec = {
  1092.         0, 250, 190, 0, 0, 0, 6,
  1093.         { 1, 0 }, { 4, -1 }, { 4, 0 }, { 4, 3 }, { PartsArrowsOn, PartsArrowsOff },
  1094.         { 1, 0 }, { 4, -1 }, { 4, 0 }, { 4, 3 }, { PartsArrowsOn, PartsArrowsOff },
  1095.         { -1, -1 }, { -1, 0 }, { 0, 4 }, { 3, 4 }, { NULL, UpPart },
  1096.         { -1, -1 }, { 1, 0 }, { 0, 4 }, { 3, 4 }, { NULL, DownPart },
  1097.         { 1, 1 }, { PartsShopGoAhead, PartsShopGoAhead },
  1098.         { 1, 1 }, { NULL, NULL },
  1099.         NULL, DrawPartsShop, 0, NULL, StartPartsShop, DonePartsShop,
  1100.         0, { 0, 0 }, NULL, 3, 1,
  1101.         COUNT_OF(flicker_on),
  1102.         flicker_on,
  1103.         flicker_off,
  1104.         push,
  1105.         COUNT_OF(mouse_areas),
  1106.         mouse_areas,
  1107.         0,
  1108.         NULL
  1109.     };
  1110.     //int result; // Pierre-Marie Baty -- unused variable
  1111.     LOG_TRACE("(%d)", pFade_away);
  1112.  
  1113.     LoadParts();
  1114.     gFade_away_parts_shop = pFade_away;
  1115.     InitialiseFlicPanel(0,
  1116.         gCurrent_graf_data->parts_image_x,
  1117.         gCurrent_graf_data->parts_image_y,
  1118.         gCurrent_graf_data->parts_image_width,
  1119.         gCurrent_graf_data->parts_image_height);
  1120.     gStart_interface_spec = &interface_spec;
  1121.     gPart_category = eParts_armour;
  1122.     gJust_bought_part = 0;
  1123.     gRefund_rate = 75;
  1124.     CalcPartsIndex();
  1125.     DoInterfaceScreen(&interface_spec, gFaded_palette, 3);
  1126.     DisposeFlicPanel(0);
  1127.     UnlockParts();
  1128.     gProgram_state.parts_shop_visited = 1;
  1129. }
  1130.  
  1131. // IDA: int __usercall AutoPartsDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
  1132. int AutoPartsDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
  1133.     LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
  1134.  
  1135.     if (pEscaped) {
  1136.         pCurrent_choice = -1;
  1137.     }
  1138.     return pCurrent_choice;
  1139. }
  1140.  
  1141. // IDA: tSO_result __cdecl DoAutoPartsShop()
  1142. tSO_result DoAutoPartsShop(void) {
  1143.     static tFlicette flicker_on[3] = {
  1144.         { 43, { 84, 168 }, { 67, 161 } },
  1145.         { 43, { 84, 168 }, { 95, 228 } },
  1146.         { 43, { 84, 168 }, { 124, 298 } },
  1147.     };
  1148.     static tFlicette flicker_off[3] = {
  1149.         { 42, { 84, 168 }, { 67, 161 } },
  1150.         { 42, { 84, 168 }, { 95, 228 } },
  1151.         { 42, { 84, 168 }, { 124, 298 } },
  1152.     };
  1153.     static tFlicette push[3] = {
  1154.         { 284, { 84, 168 }, { 67, 161 } },
  1155.         { 284, { 84, 168 }, { 95, 228 } },
  1156.         { 284, { 84, 168 }, { 124, 298 } },
  1157.     };
  1158.     static tMouse_area mouse_areas[3] = {
  1159.         { { 84, 168 }, { 32, 77 }, { 147, 294 }, { 87, 209 }, 0, 0, 0, NULL },
  1160.         { { 84, 168 }, { 95, 228 }, { 147, 294 }, { 115, 276 }, 1, 0, 0, NULL },
  1161.         { { 84, 168 }, { 124, 298 }, { 147, 294 }, { 144, 346 }, 2, 0, 0, NULL },
  1162.     };
  1163.     static tInterface_spec interface_spec = {
  1164.         0, 280, 0, 0, 0, 0, 6,
  1165.         { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { NULL, NULL },
  1166.         { -1, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { NULL, NULL },
  1167.         { -1, -1 }, { -1, 0 }, { 0, 0 }, { 2, 0 }, { NULL, NULL },
  1168.         { -1, -1 }, { 1, 0 }, { 0, 0 }, { 2, 0 }, { NULL, NULL },
  1169.         { 1, 1 }, { NULL, NULL },
  1170.         { 1, 1 }, { NULL, NULL },
  1171.         NULL, NULL, 0, NULL, NULL, AutoPartsDone, 0,
  1172.         { 0, 0 }, NULL, -1, 1,
  1173.         COUNT_OF(flicker_on), flicker_on, flicker_off, push,
  1174.         COUNT_OF(mouse_areas), mouse_areas,
  1175.         0, NULL
  1176.     };
  1177.     int result;
  1178.     LOG_TRACE("()");
  1179.  
  1180.     gProgram_state.dont_load = 1;
  1181.     result = DoInterfaceScreen(&interface_spec, 0, gProgram_state.auto_parts_reply);
  1182.     gProgram_state.dont_load = 0;
  1183.     if (result < 0) {
  1184.         RunFlic(281);
  1185.         return eSO_main_menu_invoked;
  1186.     } else {
  1187.         gProgram_state.auto_parts_reply = result;
  1188.         switch (result) {
  1189.         case eAP_auto:
  1190.             DoAutoParts();
  1191.             break;
  1192.         case eAP_manual:
  1193.             RunFlic(281);
  1194.             DoPartsShop(1);
  1195.             break;
  1196.         }
  1197.         FadePaletteDown();
  1198.         return eSO_continue;
  1199.     }
  1200. }
  1201.  
  1202. // IDA: void __cdecl SetOpponentFlic()
  1203. void SetOpponentFlic(void) {
  1204.     LOG_TRACE("()");
  1205.     ChangePanelFlic(0,
  1206.         gOpponents[gCurrent_race.opponent_list[gOpponent_index].index].mug_shot_image_data,
  1207.         gOpponents[gCurrent_race.opponent_list[gOpponent_index].index].mug_shot_image_data_length);
  1208. }
  1209.  
  1210. // IDA: void __cdecl DrawSceneyMappyInfoVieweyThing()
  1211. void DrawSceneyMappyInfoVieweyThing(void) {
  1212.     LOG_TRACE("()");
  1213.  
  1214.     RemoveTransientBitmaps(1);
  1215.     if (gProgram_state.view_type) {
  1216.         if (gProgram_state.view_type == eVT_Info) {
  1217.             ChangePanelFlic(0, gCurrent_race.info_image_data, gCurrent_race.info_image_data_length);
  1218.         } else if (gProgram_state.view_type == eVT_Opponents) {
  1219.             SetOpponentFlic();
  1220.         }
  1221.     } else {
  1222.         ChangePanelFlic(0, gCurrent_race.scene_image_data, gCurrent_race.scene_image_data_length);
  1223.     }
  1224.     TellyInImage(GetPanelPixelmap(0), gCurrent_graf_data->start_race_panel_left, gCurrent_graf_data->start_race_panel_top);
  1225. }
  1226.  
  1227. // IDA: void __cdecl DismissSceneyMappyInfoVieweyThing()
  1228. void DismissSceneyMappyInfoVieweyThing(void) {
  1229.     LOG_TRACE("()");
  1230.  
  1231.     RemoveTransientBitmaps(1);
  1232.     TellyOutImage(GetPanelPixelmap(0), gCurrent_graf_data->start_race_panel_left, gCurrent_graf_data->start_race_panel_top);
  1233. }
  1234.  
  1235. // IDA: int __usercall SelectRaceDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
  1236. int SelectRaceDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
  1237.     LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
  1238.  
  1239.     DismissSceneyMappyInfoVieweyThing();
  1240.     if (pEscaped) {
  1241.         return -1;
  1242.     }
  1243.     return pCurrent_choice;
  1244. }
  1245.  
  1246. // IDA: int __usercall StartRaceGoAhead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  1247. int StartRaceGoAhead(int* pCurrent_choice, int* pCurrent_mode) {
  1248.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  1249.  
  1250.     if (*pCurrent_choice != 1 || *pCurrent_mode) {
  1251.         return 1;
  1252.     }
  1253.     RemoveTransientBitmaps(1);
  1254.     DismissSceneyMappyInfoVieweyThing();
  1255.     gProgram_state.view_type++;
  1256.     if (gProgram_state.view_type > eVT_Opponents) {
  1257.         gProgram_state.view_type = eVT_Scene;
  1258.     }
  1259.     if (gProgram_state.view_type) {
  1260.         if (gProgram_state.view_type == eVT_Info) {
  1261.             RunFlic(210);
  1262.         } else if (gProgram_state.view_type == eVT_Opponents) {
  1263.             RunFlic(212);
  1264.         }
  1265.     } else {
  1266.         RunFlic(213);
  1267.     }
  1268.     DrawSceneyMappyInfoVieweyThing();
  1269.     return 0;
  1270. }
  1271.  
  1272. // IDA: int __usercall TryToMoveToArrows@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  1273. int TryToMoveToArrows(int* pCurrent_choice, int* pCurrent_mode) {
  1274.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  1275.  
  1276.     if (gProgram_state.view_type != eVT_Opponents) {
  1277.         return 0;
  1278.     }
  1279.     *pCurrent_choice = 5;
  1280.     *pCurrent_mode = 1;
  1281.     DRS3StartSound(gEffects_outlet, 3000);
  1282.     return 1;
  1283. }
  1284.  
  1285. // IDA: int __usercall UpOpponent@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  1286. int UpOpponent(int* pCurrent_choice, int* pCurrent_mode) {
  1287.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  1288.  
  1289. #if defined(DETHRACE_FIX_BUGS)
  1290.     // fixes bug where racers could be scrolled in other race menu modes
  1291.     if (gProgram_state.view_type != eVT_Opponents) {
  1292.         return 0;
  1293.     }
  1294. #endif
  1295.     AddToFlicQueue(gStart_interface_spec->pushed_flics[5].flic_index,
  1296.         gStart_interface_spec->pushed_flics[5].x[gGraf_data_index],
  1297.         gStart_interface_spec->pushed_flics[5].y[gGraf_data_index],
  1298.         1);
  1299.     DRS3StartSound(gEffects_outlet, 3000);
  1300.     RemoveTransientBitmaps(1);
  1301.     DropOutImageThruBottom(GetPanelPixelmap(0),
  1302.         gCurrent_graf_data->start_race_panel_left,
  1303.         gCurrent_graf_data->start_race_panel_top,
  1304.         gCurrent_graf_data->start_race_panel_top_clip,
  1305.         gCurrent_graf_data->start_race_panel_bottom_clip);
  1306.     if (gOpponent_index == 0) {
  1307.         gOpponent_index = gCurrent_race.number_of_racers;
  1308.     }
  1309.     gOpponent_index--;
  1310.     SetOpponentFlic();
  1311.     DropInImageFromTop(GetPanelPixelmap(0),
  1312.         gCurrent_graf_data->start_race_panel_left,
  1313.         gCurrent_graf_data->start_race_panel_top,
  1314.         gCurrent_graf_data->start_race_panel_top_clip,
  1315.         gCurrent_graf_data->start_race_panel_bottom_clip);
  1316.     return 0;
  1317. }
  1318.  
  1319. // IDA: int __usercall DownOpponent@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  1320. int DownOpponent(int* pCurrent_choice, int* pCurrent_mode) {
  1321.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  1322.  
  1323. #if defined(DETHRACE_FIX_BUGS)
  1324.     // fixes bug where racers could be scrolled in other race menu modes
  1325.     if (gProgram_state.view_type != eVT_Opponents) {
  1326.         return 0;
  1327.     }
  1328. #endif
  1329.     AddToFlicQueue(gStart_interface_spec->pushed_flics[6].flic_index,
  1330.         gStart_interface_spec->pushed_flics[6].x[gGraf_data_index],
  1331.         gStart_interface_spec->pushed_flics[6].y[gGraf_data_index],
  1332.         1);
  1333.     DRS3StartSound(gEffects_outlet, 3000);
  1334.     RemoveTransientBitmaps(1);
  1335.     DropOutImageThruTop(GetPanelPixelmap(0),
  1336.         gCurrent_graf_data->start_race_panel_left,
  1337.         gCurrent_graf_data->start_race_panel_top,
  1338.         gCurrent_graf_data->start_race_panel_top_clip,
  1339.         gCurrent_graf_data->start_race_panel_bottom_clip);
  1340.     gOpponent_index++;
  1341.     if (gOpponent_index == gCurrent_race.number_of_racers) {
  1342.         gOpponent_index = 0;
  1343.     }
  1344.     SetOpponentFlic();
  1345.     DropInImageFromBottom(GetPanelPixelmap(0),
  1346.         gCurrent_graf_data->start_race_panel_left,
  1347.         gCurrent_graf_data->start_race_panel_top,
  1348.         gCurrent_graf_data->start_race_panel_top_clip,
  1349.         gCurrent_graf_data->start_race_panel_bottom_clip);
  1350.     return 0;
  1351. }
  1352.  
  1353. // IDA: int __usercall UpClickOpp@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  1354. int UpClickOpp(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  1355.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  1356.  
  1357.     UpOpponent(pCurrent_choice, pCurrent_mode);
  1358.     return 0;
  1359. }
  1360.  
  1361. // IDA: int __usercall DownClickOpp@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  1362. int DownClickOpp(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  1363.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  1364.  
  1365.     DownOpponent(pCurrent_choice, pCurrent_mode);
  1366.     return 0;
  1367. }
  1368.  
  1369. // IDA: void __cdecl SelectRaceStart()
  1370. void SelectRaceStart(void) {
  1371.     LOG_TRACE("()");
  1372.  
  1373.     DrawSceneyMappyInfoVieweyThing();
  1374.     PrintMemoryDump(0, "INSIDE START RACE");
  1375. }
  1376.  
  1377. // IDA: int __cdecl SuggestRace()
  1378. int SuggestRace(void) {
  1379.     int i;
  1380.     int least_done;
  1381.     int suggested_so_far;
  1382.     int suggested_race;
  1383.     int new_suggestion;
  1384.     int number_of_visits;
  1385.     LOG_TRACE("()");
  1386.  
  1387.     suggested_so_far = 32767;
  1388.     suggested_race = 0;
  1389.     if (gProgram_state.game_completed) {
  1390.         return IRandomBetween(0, gNumber_of_races - 1);
  1391.     }
  1392.     if (gProgram_state.redo_race_index >= 0) {
  1393.         return gProgram_state.redo_race_index;
  1394.     }
  1395.     for (i = 0; i < gNumber_of_races; i++) {
  1396.         if (gRace_list[i].suggested_rank <= suggested_so_far && gRace_list[i].suggested_rank >= gProgram_state.rank) {
  1397.             suggested_so_far = gRace_list[i].suggested_rank;
  1398.             suggested_race = i;
  1399.         }
  1400.     }
  1401.     number_of_visits = gRace_list[suggested_race].been_there_done_that;
  1402.     new_suggestion = suggested_race;
  1403.  
  1404.     if (number_of_visits) {
  1405.         // Jeff: if we have already completed the suggested race, look backwards for a race that we haven't completed as many times
  1406.         for (i = suggested_race - 1; i >= 0 && i >= suggested_race - 5; i--) {
  1407.             if (gRace_list[i].rank_required < gProgram_state.rank || gRace_list[i].best_rank > gProgram_state.rank) {
  1408.                 continue;
  1409.             }
  1410.             least_done = gRace_list[i].been_there_done_that < number_of_visits;
  1411.             if (!gRace_list[i].been_there_done_that
  1412.                 || (least_done && suggested_race - 3 <= i)) {
  1413.                 new_suggestion = i;
  1414.                 number_of_visits = gRace_list[i].been_there_done_that;
  1415.             }
  1416.         }
  1417.         // Jeff: look forwards for a race that we haven't completed as many times as the previous suggestion
  1418.         for (i = suggested_race + 1; i < gNumber_of_races; i++) {
  1419.             least_done = gRace_list[i].been_there_done_that < number_of_visits;
  1420.             if (!least_done) {
  1421.                 continue;
  1422.             }
  1423.  
  1424.             if ((gRace_list[i].rank_required >= gProgram_state.rank && gRace_list[i].best_rank <= gProgram_state.rank)
  1425.                 || gProgram_state.game_completed) {
  1426.                 new_suggestion = i;
  1427.                 number_of_visits = gRace_list[i].been_there_done_that;
  1428.             }
  1429.         }
  1430.     }
  1431.     return new_suggestion;
  1432. }
  1433.  
  1434. // IDA: void __usercall SelectRaceDraw(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
  1435. void SelectRaceDraw(int pCurrent_choice, int pCurrent_mode) {
  1436.     tOpponent* the_opponent;
  1437.     tText_chunk* the_chunk;
  1438.     int j;
  1439.     int k;
  1440.     int y_coord;
  1441.     //char s[256]; // Pierre-Marie Baty -- unused variable
  1442.     //char temp_str[256]; // Pierre-Marie Baty -- unused variable
  1443.     //char* sub_pt; // Pierre-Marie Baty -- unused variable
  1444.     //char sub_str[16]; // Pierre-Marie Baty -- unused variable
  1445.     tU32* test;
  1446.     static tU32 test2;
  1447.     LOG_TRACE8("(%d, %d)", pCurrent_choice, pCurrent_mode);
  1448.  
  1449.     if (gProgram_state.view_type == eVT_Opponents) {
  1450.         the_opponent = &gOpponents[gCurrent_race.opponent_list[gOpponent_index].index];
  1451.         for (j = 0; j < the_opponent->text_chunk_count; j++) {
  1452.             the_chunk = &the_opponent->text_chunks[j];
  1453.             if (GetPanelFlicFrameIndex(0) >= the_chunk->frame_cue && GetPanelFlicFrameIndex(0) < the_chunk->frame_end) {
  1454.                 y_coord = the_chunk->y_coord * gGraf_specs[gGraf_spec_index].total_height / 200
  1455.                     + gCurrent_graf_data->start_race_panel_top;
  1456.                 for (k = 0; k < the_chunk->line_count; k++) {
  1457.                     TransBrPixelmapText(
  1458.                         gBack_screen,
  1459.                         the_chunk->x_coord * gGraf_specs[gGraf_spec_index].total_width / 320
  1460.                             + gCurrent_graf_data->start_race_panel_left,
  1461.                         y_coord,
  1462.                         201,
  1463.                         gFont_7,
  1464.                         the_chunk->text[k]);
  1465.                     y_coord += gFont_7->glyph_y + gFont_7->glyph_y / 2;
  1466.                 }
  1467.             }
  1468.         }
  1469.     } else if (gProgram_state.view_type == eVT_Info) {
  1470.         for (j = 0; j < gCurrent_race.text_chunk_count; j++) {
  1471.             the_chunk = &gCurrent_race.text_chunks[j];
  1472.             if (GetPanelFlicFrameIndex(0) >= the_chunk->frame_cue && GetPanelFlicFrameIndex(0) < the_chunk->frame_end) {
  1473.                 y_coord = the_chunk->y_coord * gGraf_specs[gGraf_spec_index].total_height / 200
  1474.                     + gCurrent_graf_data->start_race_panel_top;
  1475.                 for (k = 0; k < the_chunk->line_count; k++) {
  1476.                     TransBrPixelmapText(
  1477.                         gBack_screen,
  1478.                         the_chunk->x_coord * gGraf_specs[gGraf_spec_index].total_width / 320
  1479.                             + gCurrent_graf_data->start_race_panel_left,
  1480.                         y_coord,
  1481.                         201,
  1482.                         gFont_7,
  1483.                         the_chunk->text[k]);
  1484.                     y_coord += gFont_7->glyph_y + gFont_7->glyph_y / 2;
  1485.                 }
  1486.             }
  1487.         }
  1488.     }
  1489.     test = KevKeyService();
  1490.     if (*test) {
  1491.         test2 = *test;
  1492.     }
  1493.     if (test[0] == 0x27645433 && test[1] == 0x758f0015) {
  1494.         // cheat code: "KEVWOZEAR"
  1495.         gProgram_state.game_completed = 1;
  1496.         DRS3StartSound(gEffects_outlet, 3202);
  1497.         DRS3StartSound(gEffects_outlet, 3202);
  1498.     }
  1499.     if (test[0] == 0x33f75455 && test[1] == 0xC10AAAF2) {
  1500.         // cheat code: "IWANTTOFIDDLE"
  1501.  
  1502.         char s[128];
  1503.         FILE* f;
  1504.         size_t i; // Pierre-Marie Baty -- fixed type
  1505.  
  1506.         // Jeff
  1507.         assert(strlen(gApplication_path) < 120);
  1508.  
  1509.         PathCat(s, gApplication_path, "ACTORS");
  1510.         PathCat(s, s, "PROG.ACT");
  1511.         PDFileUnlock(s);
  1512.         f = fopen(s, "wb");
  1513.         if (f != NULL) {
  1514.             DRS3StartSound(gEffects_outlet, 9000);
  1515.             if (gDecode_thing) {
  1516.                 for (i = 0; i < strlen(gDecode_string); i++) {
  1517.                     gDecode_string[i] -= 50;
  1518.                 }
  1519.                 fputs(gDecode_string, f);
  1520.                 for (i = 0; i < strlen(gDecode_string); i++) {
  1521.                     gDecode_string[i] += 50;
  1522.                 }
  1523.             } else {
  1524.                 for (i = 0; i < 20; i++) {
  1525.                     fputs("*************", f);
  1526.                 }
  1527.             }
  1528.             gDecode_thing ^= 0x40u;
  1529.             fclose(f);
  1530.             EncodeAllFilesInDirectory("");
  1531.             EncodeAllFilesInDirectory("CARS");
  1532.             EncodeAllFilesInDirectory("NONCARS");
  1533.             EncodeAllFilesInDirectory("RACES");
  1534.             EncodeAllFilesInDirectory("32X20X8");
  1535.             EncodeAllFilesInDirectory("64X48X8");
  1536.             PathCat(s, "32X20X8", "CARS");
  1537.             EncodeAllFilesInDirectory(s);
  1538.             PathCat(s, "64X48X8", "CARS");
  1539.             EncodeAllFilesInDirectory(s);
  1540.             PathCat(s, "32X20X8", "FONTS");
  1541.             EncodeAllFilesInDirectory(s);
  1542.             PathCat(s, "64X48X8", "FONTS");
  1543.             EncodeAllFilesInDirectory(s);
  1544.         }
  1545.         DRS3StartSound(gEffects_outlet, 9000);
  1546.     }
  1547. }
  1548.  
  1549. // IDA: tSO_result __usercall DoSelectRace@<EAX>(int *pSecond_time_around@<EAX>)
  1550. tSO_result DoSelectRace(int* pSecond_time_around) {
  1551.     static tFlicette flicker_on[7] = {
  1552.         { 43, { 224, 448 }, { 28, 67 } },
  1553.         { 43, { 224, 448 }, { 56, 134 } },
  1554.         { 43, { 224, 448 }, { 85, 204 } },
  1555.         { 43, { 224, 448 }, { 113, 271 } },
  1556.         { 43, { 224, 448 }, { 147, 353 } },
  1557.         { 221, { 30, 60 }, { 79, 190 } },
  1558.         { 221, { 30, 60 }, { 79, 190 } }
  1559.     };
  1560.  
  1561.     static tFlicette flicker_off[7] = {
  1562.         { 42, { 224, 448 }, { 28, 67 } },
  1563.         { 42, { 224, 448 }, { 56, 134 } },
  1564.         { 42, { 224, 448 }, { 85, 204 } },
  1565.         { 42, { 224, 448 }, { 113, 271 } },
  1566.         { 42, { 224, 448 }, { 147, 353 } },
  1567.         { 220, { 30, 60 }, { 79, 190 } },
  1568.         { 220, { 30, 60 }, { 79, 190 } }
  1569.     };
  1570.  
  1571.     static tFlicette push[7] = {
  1572.         { 195, { 224, 448 }, { 28, 67 } },
  1573.         { -1, { 224, 448 }, { 56, 134 } },
  1574.         { 200, { 224, 448 }, { 85, 204 } },
  1575.         { 202, { 224, 448 }, { 113, 271 } },
  1576.         { 201, { 224, 448 }, { 147, 353 } },
  1577.         { 222, { 30, 60 }, { 79, 190 } },
  1578.         { 225, { 30, 60 }, { 119, 286 } }
  1579.     };
  1580.  
  1581.     static tMouse_area mouse_areas[7] = {
  1582.         { { 224, 448 }, { 28, 67 }, { 287, 574 }, { 48, 115 }, 0, 0, 0, NULL },
  1583.         { { 224, 448 }, { 56, 134 }, { 287, 574 }, { 76, 182 }, 1, 0, 0, NULL },
  1584.         { { 224, 448 }, { 85, 204 }, { 287, 574 }, { 105, 252 }, 2, 0, 0, NULL },
  1585.         { { 224, 448 }, { 113, 271 }, { 287, 574 }, { 133, 319 }, 3, 0, 0, NULL },
  1586.         { { 224, 448 }, { 147, 353 }, { 287, 574 }, { 167, 401 }, 4, 0, 0, NULL },
  1587.         { { 30, 60 },
  1588.             { 79, 190 },
  1589.             { 45, 90 },
  1590.             { 106, 254 },
  1591.             -1,
  1592.             0,
  1593.             0,
  1594.             &UpClickOpp },
  1595.         { { 30, 60 },
  1596.             { 119, 286 },
  1597.             { 45, 90 },
  1598.             { 146, 350 },
  1599.             -1,
  1600.             0,
  1601.             0,
  1602.             &DownClickOpp }
  1603.     };
  1604.  
  1605.     static tInterface_spec interface_spec = {
  1606.         0,                            // initial_imode
  1607.         191,                          // first_opening_flic
  1608.         190,                          // second_opening_flic
  1609.         0,                            // end_flic_go_ahead
  1610.         0,                            // end_flic_escaped
  1611.         0,                            // end_flic_otherwise
  1612.         6,                            // flic_bunch_to_load
  1613.         { -1, 0 },                    // move_left_new_mode
  1614.         { 0, -4 },                    // move_left_delta
  1615.         { 0, 1 },                     // move_left_min
  1616.         { 0, 1 },                     // move_left_max
  1617.         { &TryToMoveToArrows, NULL }, // move_left_proc
  1618.         { -1, 0 },                    // move_right_new_mode
  1619.         { 0, -4 },                    // move_right_delta
  1620.         { 0, 1 },                     // move_right_min
  1621.         { 0, 1 },                     // move_right_max
  1622.         { &TryToMoveToArrows, NULL }, // move_right_proc
  1623.         { -1, -1 },                   // move_up_new_mode
  1624.         { -1, 0 },                    // move_up_delta
  1625.         { 0, 5 },                     // move_up_min
  1626.         { 4, 5 },                     // move_up_max
  1627.         { NULL, &UpOpponent },        // move_up_proc
  1628.         { -1, -1 },                   // move_down_new_mode
  1629.         { 1, 0 },                     // move_down_delta
  1630.         { 0, 5 },                     // move_down_min
  1631.         { 4, 5 },                     // move_down_max
  1632.         { NULL, &DownOpponent },      // move_down_proc
  1633.         { 1, 1 },                     // go_ahead_allowed
  1634.         { &StartRaceGoAhead, NULL },  // go_ahead_proc
  1635.         { 1, 1 },                     // escape_allowed
  1636.         { NULL, NULL },               // escape_proc
  1637.         NULL,                         // exit_proc
  1638.         &SelectRaceDraw,              // draw_proc
  1639.         0u,                           // time_out
  1640.         NULL,                         // start_proc1
  1641.         &SelectRaceStart,             // start_proc2
  1642.         &SelectRaceDone,              // done_proc
  1643.         0,                            // font_needed
  1644.         { 0, 0 },                     // typeable
  1645.         NULL,                         // get_original_string
  1646.         -1,                           // escape_code
  1647.         1,                            // dont_save_or_load
  1648.         7,                            // number_of_button_flics
  1649.         flicker_on,                   // flicker_on_flics
  1650.         flicker_off,                  // flicker_off_flics
  1651.         push,                         // pushed_flics
  1652.         7,                            // number_of_mouse_areas
  1653.         mouse_areas,                  // mouse_areas
  1654.         0,                            // number_of_recopy_areas
  1655.         NULL                          // recopy_areas
  1656.     };
  1657.  
  1658.     int result;
  1659.     //int default_choice; // Pierre-Marie Baty -- unused variable
  1660.     int suggested;
  1661.     int old_current_race;
  1662.     LOG_TRACE("(%p)", pSecond_time_around);
  1663.  
  1664.     suggested = SuggestRace();
  1665.     if (!*pSecond_time_around) {
  1666.         gProgram_state.current_race_index = suggested;
  1667.         SelectOpponents(&gCurrent_race);
  1668.     }
  1669.     gProgram_state.parts_shop_visited = 0;
  1670.     gProgram_state.dont_load = 1;
  1671.     gDeceased_image = LoadPixelmap("DECEASED.PIX");
  1672.     InitialiseFlicPanel(
  1673.         0,
  1674.         gCurrent_graf_data->start_race_panel_left,
  1675.         gCurrent_graf_data->start_race_panel_top,
  1676.         gCurrent_graf_data->start_race_panel_right - gCurrent_graf_data->start_race_panel_left,
  1677.         gCurrent_graf_data->start_race_panel_bottom - gCurrent_graf_data->start_race_panel_top);
  1678.     LoadRaceInfo(gProgram_state.current_race_index, &gCurrent_race);
  1679.     do {
  1680.         gProgram_state.view_type = 0;
  1681.         gOpponent_index = 0;
  1682.         gStart_interface_spec = &interface_spec;
  1683.         if (gFaded_palette || gCurrent_splash) {
  1684.             result = DoInterfaceScreen(&interface_spec, 1, 4);
  1685.         } else {
  1686.             result = DoInterfaceScreen(&interface_spec, 0, 4);
  1687.         }
  1688.  
  1689.         if (result == 0 || result == 2 || result == 3) {
  1690.             DisposeFlicPanel(0);
  1691.  
  1692.             if (result == 2) {
  1693.                 if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
  1694.                     DoFeatureUnavailableInDemo();
  1695.                 } else {
  1696.                     RunFlic(192);
  1697.                     DoPartsShop(0);
  1698.                 }
  1699.             } else if (result == 3) {
  1700.                 RunFlic(192);
  1701.                 DoChangeCar();
  1702.             } else if (result == 0) {
  1703.                 RunFlic(192);
  1704.                 old_current_race = gProgram_state.current_race_index;
  1705.                 DoChangeRace();
  1706.                 if (gProgram_state.current_race_index != old_current_race) {
  1707.                     DisposeRaceInfo(&gCurrent_race);
  1708.                     SelectOpponents(&gCurrent_race);
  1709.                     LoadRaceInfo(gProgram_state.current_race_index, &gCurrent_race);
  1710.                 }
  1711.             }
  1712.             InitialiseFlicPanel(
  1713.                 0,
  1714.                 gCurrent_graf_data->start_race_panel_left,
  1715.                 gCurrent_graf_data->start_race_panel_top,
  1716.                 gCurrent_graf_data->start_race_panel_right - gCurrent_graf_data->start_race_panel_left,
  1717.                 gCurrent_graf_data->start_race_panel_bottom - gCurrent_graf_data->start_race_panel_top);
  1718.         }
  1719.     } while (result >= 0 && result != 4);
  1720.     BrPixelmapFree(gDeceased_image);
  1721.     FillInRaceInfo(&gCurrent_race);
  1722.     DisposeRaceInfo(&gCurrent_race);
  1723.     DisposeFlicPanel(0);
  1724.     *pSecond_time_around = 1;
  1725.     gProgram_state.dont_load = 0;
  1726.     if (result >= 0) {
  1727.         *pSecond_time_around = 1;
  1728.         if (gProgram_state.parts_shop_visited || !PartsShopRecommended()) {
  1729.             FadePaletteDown();
  1730.             return eSO_continue;
  1731.         } else {
  1732.             RunFlic(192);
  1733.             return DoAutoPartsShop();
  1734.         }
  1735.     } else {
  1736.         RunFlic(192);
  1737.         gDisallow_abandon_race = 1;
  1738.         return eSO_main_menu_invoked;
  1739.     }
  1740. }
  1741.  
  1742. // IDA: void __usercall DrawGridCar(int pX@<EAX>, int pY@<EDX>, br_pixelmap *pImage@<EBX>)
  1743. void DrawGridCar(int pX, int pY, br_pixelmap* pImage) {
  1744.     LOG_TRACE("(%d, %d, %p)", pX, pY, pImage);
  1745.  
  1746.     if (gCurrent_graf_data->grid_left_clip <= pX && pX + pImage->width < gCurrent_graf_data->grid_right_clip) {
  1747.         DRPixelmapRectangleMaskedCopy(gBack_screen, pX, pY, pImage, 0, 0, pImage->width, pImage->height);
  1748.     }
  1749. }
  1750.  
  1751. // IDA: void __usercall DrawGrid(int pOffset@<EAX>, int pDraw_it@<EDX>)
  1752. void DrawGrid(int pOffset, int pDraw_it) {
  1753.     int i;
  1754.     int j;
  1755.     int y;
  1756.     int x;
  1757.     int str_x;
  1758.     //int width_job; // Pierre-Marie Baty -- unused variable
  1759.     int done_highest;
  1760.     int str_index;
  1761.     int swap_1_x = 0;
  1762.     int swap_1_y = 0;
  1763.     int swap_2_x = 0;
  1764.     int swap_2_y = 0;
  1765.     br_pixelmap* the_image = NULL;
  1766.     br_pixelmap* swap_1_image = NULL;
  1767.     br_pixelmap* swap_2_image = NULL;
  1768.     char numbers_str[4][100];
  1769.     char total_str[256];
  1770.     tU32 the_time;
  1771.     LOG_TRACE("(%d, %d)", pOffset, pDraw_it);
  1772.  
  1773.     done_highest = 0;
  1774.     str_index = 0;
  1775.  
  1776.     memset(numbers_str, 0, sizeof(numbers_str));
  1777.     memset(total_str, 0, sizeof(total_str));
  1778.  
  1779.     the_time = PDGetTotalTime();
  1780.     BrPixelmapRectangleFill(
  1781.         gBack_screen,
  1782.         gCurrent_graf_data->grid_left_clip,
  1783.         gCurrent_graf_data->grid_top_clip,
  1784.         gCurrent_graf_data->grid_right_clip - gCurrent_graf_data->grid_left_clip,
  1785.         gCurrent_graf_data->grid_bottom_clip - gCurrent_graf_data->grid_top_clip,
  1786.         0);
  1787.     for (i = 0; i < gCurrent_race.number_of_racers; i++) {
  1788.         if (gCurrent_race.opponent_list[i].index == -1) {
  1789.             the_image = gProgram_state.current_car.grid_icon_image;
  1790.         } else {
  1791.             the_image = gCurrent_race.opponent_list[i].car_spec->grid_icon_image;
  1792.         }
  1793.         y = gCurrent_graf_data->grid_top_y
  1794.             + i % 2 * gCurrent_graf_data->grid_y_pitch
  1795.             - the_image->height / 2
  1796.             - gGrid_y_adjust;
  1797.         x = gCurrent_graf_data->grid_left_x
  1798.             + i % 2 * gCurrent_graf_data->grid_x_stagger
  1799.             + i / 2 * gCurrent_graf_data->grid_x_pitch
  1800.             - pOffset;
  1801.         if (i == gSwap_grid_1) {
  1802.             swap_1_x = x;
  1803.             swap_1_y = y;
  1804.             swap_1_image = the_image;
  1805.         } else if (i == gSwap_grid_2) {
  1806.             swap_2_x = x;
  1807.             swap_2_y = y;
  1808.             swap_2_image = the_image;
  1809.         } else if (gCurrent_race.opponent_list[i].index != -1 || the_time % 900 / 300) {
  1810.             DrawGridCar(x, y, the_image);
  1811.         }
  1812.         if (!done_highest && gCurrent_race.opponent_list[i].ranking >= gProgram_state.rank) {
  1813.             done_highest = 1;
  1814.             if (x - gCurrent_graf_data->grid_marker_margin >= gCurrent_graf_data->grid_left_clip
  1815.                 && x + the_image->width < gCurrent_graf_data->grid_right_clip) {
  1816.                 BrPixelmapLine(
  1817.                     gBack_screen,
  1818.                     x - gCurrent_graf_data->grid_marker_margin,
  1819.                     y - gCurrent_graf_data->grid_marker_margin,
  1820.                     x - gCurrent_graf_data->grid_marker_margin,
  1821.                     y + gCurrent_graf_data->grid_marker_margin + the_image->height,
  1822.                     45);
  1823.                 BrPixelmapLine(
  1824.                     gBack_screen,
  1825.                     x - gCurrent_graf_data->grid_marker_margin,
  1826.                     y - gCurrent_graf_data->grid_marker_margin,
  1827.                     x + gCurrent_graf_data->grid_marker_x_len,
  1828.                     y - gCurrent_graf_data->grid_marker_margin,
  1829.                     45);
  1830.                 BrPixelmapLine(
  1831.                     gBack_screen,
  1832.                     x - gCurrent_graf_data->grid_marker_margin,
  1833.                     y + gCurrent_graf_data->grid_marker_margin + the_image->height,
  1834.                     x + gCurrent_graf_data->grid_marker_x_len,
  1835.                     y + gCurrent_graf_data->grid_marker_margin + the_image->height,
  1836.                     45);
  1837.             }
  1838.         }
  1839.         if (gCurrent_race.opponent_list[i].index < 0) {
  1840.             str_index = 2;
  1841.         } else if (gProgram_state.rank <= gCurrent_race.opponent_list[i].ranking) {
  1842.             if (str_index >= 2) {
  1843.                 str_index = 3;
  1844.             } else {
  1845.                 str_index = 1;
  1846.             }
  1847.         }
  1848.         if (gCurrent_race.opponent_list[i].index >= 0) {
  1849.             if (gOpponents[gCurrent_race.opponent_list[i].index].car_number <= 0
  1850.                 || gOpponents[gCurrent_race.opponent_list[i].index].car_number >= 100) {
  1851.                 if (gOpponents[gCurrent_race.opponent_list[i].index].car_number < 0) {
  1852.                     sprintf(&numbers_str[str_index][strlen(numbers_str[str_index])], "%c ", ':');
  1853.                 }
  1854.             } else {
  1855.                 sprintf(
  1856.                     &numbers_str[str_index][strlen(numbers_str[str_index])],
  1857.                     "%d ",
  1858.                     gOpponents[gCurrent_race.opponent_list[i].index].car_number);
  1859.             }
  1860.         } else {
  1861.             if (gProgram_state.frank_or_anniness == eAnnie) {
  1862.                 sprintf(&numbers_str[str_index][strlen(numbers_str[str_index])], "%c ", '<');
  1863.             } else {
  1864.                 sprintf(&numbers_str[str_index][strlen(numbers_str[str_index])], "%c ", ';');
  1865.             }
  1866.         }
  1867.     }
  1868.     if (gSwap_grid_1 >= 0) {
  1869.         DrawGridCar(
  1870.             gGrid_transition_stage * (swap_2_x - swap_1_x) / 100 + swap_1_x,
  1871.             gGrid_transition_stage * (swap_2_y - swap_1_y) / 100 + swap_1_y,
  1872.             swap_1_image);
  1873.         DrawGridCar(
  1874.             gGrid_transition_stage * (swap_1_x - swap_2_x) / 100 + swap_2_x,
  1875.             gGrid_transition_stage * (swap_1_y - swap_2_y) / 100 + swap_2_y,
  1876.             swap_2_image);
  1877.     }
  1878.     if (gDraw_grid_status == eGrid_draw_all) {
  1879.         if (strlen(numbers_str[3])) {
  1880.             numbers_str[3][strlen(numbers_str[3]) - 1] = '\0';
  1881.         } else {
  1882.             numbers_str[2][strlen(numbers_str[2]) - 1] = '\0';
  1883.         }
  1884.         strcpy(total_str, numbers_str[0]);
  1885.         for (i = 1; i < COUNT_OF(numbers_str); i++) {
  1886.             strcat(total_str, numbers_str[i]);
  1887.         }
  1888.         str_x = (gCurrent_graf_data->grid_numbers_left + gCurrent_graf_data->grid_numbers_right) / 2
  1889.             - (BrPixelmapTextWidth(gBack_screen, gBig_font, total_str) / 2);
  1890.         BrPixelmapRectangleFill(
  1891.             gBack_screen,
  1892.             gCurrent_graf_data->grid_numbers_left,
  1893.             gCurrent_graf_data->grid_numbers_top,
  1894.             gCurrent_graf_data->grid_numbers_right - gCurrent_graf_data->grid_numbers_left,
  1895.             gBig_font->glyph_y,
  1896.             0);
  1897.         gGrid_number_x_coords[0] = str_x - 3 - gCurrent_graf_data->grid_numbers_left;
  1898.         for (i = 0; i < 4; ++i) {
  1899.             if (i != 2 || the_time % 900 / 300) {
  1900.                 TransBrPixelmapText(
  1901.                     gBack_screen,
  1902.                     str_x,
  1903.                     gCurrent_graf_data->grid_numbers_top,
  1904.                     gGrid_number_colour[i],
  1905.                     gBig_font,
  1906.                     numbers_str[i]);
  1907.             }
  1908.             str_x += BrPixelmapTextWidth(gBack_screen, gBig_font, numbers_str[i]);
  1909.         }
  1910.         for (i = gCurrent_race.number_of_racers - 1; i > 0; i--) {
  1911.             for (j = strlen(total_str) - 2; j >= 0; j--) {
  1912.                 if (total_str[j] == ' ') {
  1913.                     total_str[j + 1] = '\0';
  1914.                     break;
  1915.                 }
  1916.             }
  1917.             gGrid_number_x_coords[i] = gGrid_number_x_coords[0] + BrPixelmapTextWidth(gBack_screen, gBig_font, total_str);
  1918.         }
  1919.     }
  1920.     if (pDraw_it) {
  1921.         PDScreenBufferSwap(0);
  1922.     }
  1923. }
  1924.  
  1925. // IDA: void __usercall MoveGrid(int pFrom@<EAX>, int pTo@<EDX>, tS32 pTime_to_move@<EBX>)
  1926. void MoveGrid(int pFrom, int pTo, tS32 pTime_to_move) {
  1927.     tS32 start_time;
  1928.     tS32 the_time;
  1929.     //int move_distance; // Pierre-Marie Baty -- unused variable
  1930.     int pitch;
  1931.     LOG_TRACE("(%d, %d, %d)", pFrom, pTo, pTime_to_move);
  1932.  
  1933.     pitch = gCurrent_graf_data->grid_x_pitch;
  1934.     start_time = PDGetTotalTime();
  1935.     while (1) {
  1936.         the_time = PDGetTotalTime();
  1937.         if (start_time + pTime_to_move <= the_time) {
  1938.             break;
  1939.         }
  1940.         DrawGrid(pitch * pFrom + pitch * (pTo - pFrom) * (the_time - start_time) / pTime_to_move, 1);
  1941.     }
  1942.     DrawGrid(pitch * pTo, 1);
  1943. }
  1944.  
  1945. // IDA: int __usercall CalcGridOffset@<EAX>(int pPosition@<EAX>)
  1946. int CalcGridOffset(int pPosition) {
  1947.     LOG_TRACE("(%d)", pPosition);
  1948.  
  1949.     return pPosition / 2 - 1;
  1950. }
  1951.  
  1952. // IDA: void __usercall GridDraw(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
  1953. void GridDraw(int pCurrent_choice, int pCurrent_mode) {
  1954.     LOG_TRACE8("(%d, %d)", pCurrent_choice, pCurrent_mode);
  1955.  
  1956.     if (gDraw_grid_status > eGrid_draw_none) {
  1957.         DrawGrid(gCurrent_graf_data->grid_x_pitch * CalcGridOffset(gOur_starting_position), 0);
  1958.     }
  1959. }
  1960.  
  1961. // IDA: void __usercall ActuallySwapOrder(int pFirst_index@<EAX>, int pSecond_index@<EDX>)
  1962. void ActuallySwapOrder(int pFirst_index, int pSecond_index) {
  1963.     tOpp_spec temp_opp;
  1964.     LOG_TRACE("(%d, %d)", pFirst_index, pSecond_index);
  1965.  
  1966.     temp_opp = gCurrent_race.opponent_list[pFirst_index];
  1967.     gCurrent_race.opponent_list[pFirst_index] = gCurrent_race.opponent_list[pSecond_index];
  1968.     gCurrent_race.opponent_list[pSecond_index] = temp_opp;
  1969.     gOur_starting_position = pSecond_index;
  1970.  
  1971.     // LOG_DEBUG("first %d, second %d", gCurrent_race.opponent_list[pFirst_index].index, gCurrent_race.opponent_list[pSecond_index].index);
  1972. }
  1973.  
  1974. // IDA: void __usercall DoGridTransition(int pFirst_index@<EAX>, int pSecond_index@<EDX>)
  1975. void DoGridTransition(int pFirst_index, int pSecond_index) {
  1976.     tU32 start_time;
  1977.     tU32 the_time;
  1978.     LOG_TRACE("(%d, %d)", pFirst_index, pSecond_index);
  1979.  
  1980.     if (pFirst_index != pSecond_index) {
  1981.         start_time = PDGetTotalTime();
  1982.         gSwap_grid_1 = pFirst_index;
  1983.         gSwap_grid_2 = pSecond_index;
  1984.         while (1) {
  1985.             the_time = PDGetTotalTime();
  1986.             if (start_time + 150 <= the_time) {
  1987.                 break;
  1988.             }
  1989.             RemoveTransientBitmaps(1);
  1990.             gGrid_transition_stage = 100 * (the_time - start_time) / 150;
  1991.             DrawGrid(gCurrent_graf_data->grid_x_pitch * CalcGridOffset(gOur_starting_position), 0);
  1992.             ProcessFlicQueue(gFrame_period);
  1993.             DoMouseCursor();
  1994.             PDScreenBufferSwap(0);
  1995.         }
  1996.         gSwap_grid_1 = -1;
  1997.         gSwap_grid_2 = -1;
  1998.         ActuallySwapOrder(pFirst_index, pSecond_index);
  1999.         MoveGrid(CalcGridOffset(pFirst_index), CalcGridOffset(pSecond_index), 150);
  2000.     }
  2001. }
  2002.  
  2003. // IDA: void __cdecl ChallengeStart()
  2004. void ChallengeStart(void) {
  2005.     br_pixelmap* the_map;
  2006.     int i;
  2007.     int j;
  2008.     int line_count;
  2009.     int dare_index;
  2010.     FILE* f;
  2011.     tPath_name the_path;
  2012.     char s[256];
  2013.     LOG_TRACE("()");
  2014.  
  2015.     InitialiseFlicPanel(
  2016.         0,
  2017.         gCurrent_graf_data->start_race_panel_left,
  2018.         gCurrent_graf_data->start_race_panel_top,
  2019.         gCurrent_graf_data->start_race_panel_right - gCurrent_graf_data->start_race_panel_left,
  2020.         gCurrent_graf_data->start_race_panel_bottom - gCurrent_graf_data->start_race_panel_top);
  2021.     ChangePanelFlic(
  2022.         0,
  2023.         gOpponents[gChallenger_index__racestrt].mug_shot_image_data,
  2024.         gOpponents[gChallenger_index__racestrt].mug_shot_image_data_length);
  2025.     if (gScreen->row_bytes < 0) {
  2026.         BrFatal("C:\\Msdev\\Projects\\DethRace\\Racestrt.c", 2610, "Bruce bug at line %d, file C:\\Msdev\\Projects\\DethRace\\Racestrt.c", 50);
  2027.     }
  2028.     the_map = DRPixelmapAllocate(
  2029.         gScreen->type,
  2030.         gCurrent_graf_data->dare_mugshot_width,
  2031.         gCurrent_graf_data->dare_mugshot_height,
  2032.         0,
  2033.         0);
  2034.  
  2035.     BrPixelmapRectangleCopy(the_map, 0, 0, GetPanelPixelmap(0), gCurrent_graf_data->dare_mug_left_margin, gCurrent_graf_data->dare_mug_top_margin, gCurrent_graf_data->dare_mugshot_width, gCurrent_graf_data->dare_mugshot_height);
  2036.     DisposeFlicPanel(0);
  2037.     TellyInImage(the_map, gCurrent_graf_data->dare_mugshot_left, gCurrent_graf_data->dare_mugshot_top);
  2038.     BrPixelmapFree(the_map);
  2039.     the_map = DRPixelmapAllocate(gScreen->type, gCurrent_graf_data->dare_text_width, gCurrent_graf_data->dare_mugshot_height, 0, 0);
  2040.     BrPixelmapFill(the_map, 0);
  2041.     TransBrPixelmapText(the_map, 0, 0, 1u, gBig_font, gOpponents[gChallenger_index__racestrt].abbrev_name);
  2042.     PathCat(the_path, gApplication_path, "DARES.TXT");
  2043.     f = DRfopen(the_path, "rt");
  2044.     if (f == NULL) {
  2045.         FatalError(kFatalError_OpenDareTxt);
  2046.     }
  2047.  
  2048.     dare_index = IRandomBetween(0, GetAnInt(f) - 1);
  2049.     for (i = 0; i < dare_index; i++) {
  2050.         line_count = GetAnInt(f);
  2051.         for (j = 0; j < line_count; j++) {
  2052.             GetALineAndDontArgue(f, s);
  2053.         }
  2054.     }
  2055.     line_count = GetAnInt(f);
  2056.     for (i = 0; i < line_count; i++) {
  2057.         GetALineAndDontArgue(f, s);
  2058.         TransBrPixelmapText(the_map, 0, 2 * (i + 1) * gBig_font->glyph_y, 0x86u, gBig_font, s);
  2059.     }
  2060.     fclose(f);
  2061.     BrPixelmapLine(the_map, 0, gBig_font->glyph_y + 2, the_map->width, gBig_font->glyph_y + 2, 45);
  2062.     TellyInImage(the_map, gCurrent_graf_data->dare_text_left, gCurrent_graf_data->dare_mugshot_top);
  2063.     BrPixelmapFree(the_map);
  2064.     UnlockOpponentMugshot(gChallenger_index__racestrt);
  2065.     gDare_start_time = PDGetTotalTime();
  2066. }
  2067.  
  2068. // IDA: int __usercall CheckNextStage@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  2069. int CheckNextStage(int* pCurrent_choice, int* pCurrent_mode) {
  2070.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  2071.  
  2072.     if (gDare_start_time && (unsigned int)(PDGetTotalTime() - gDare_start_time) >= 7500) {
  2073.         BrPixelmapRectangleFill(
  2074.             gBack_screen,
  2075.             gCurrent_graf_data->grid_left_clip,
  2076.             gCurrent_graf_data->dare_mugshot_top,
  2077.             gCurrent_graf_data->grid_right_clip - gCurrent_graf_data->grid_left_clip,
  2078.             gCurrent_graf_data->dare_mugshot_height,
  2079.             0);
  2080.         DoGridTransition(gOur_starting_position, gChallenger_position);
  2081.         gDraw_grid_status = eGrid_draw_icons_only;
  2082.         gDare_start_time = 0;
  2083.     }
  2084.     return 0;
  2085. }
  2086.  
  2087. // IDA: int __usercall ChallengeDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
  2088. int ChallengeDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
  2089.     LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
  2090.  
  2091.     if (!pEscaped || gDare_start_time) {
  2092.         if (!pEscaped && gDare_start_time) {
  2093.             ActuallySwapOrder(gOur_starting_position, gChallenger_position);
  2094.             ChallengeOccurred(gChallenger_index__racestrt, 1);
  2095.         }
  2096.     } else {
  2097.         DoGridTransition(gOur_starting_position, gOriginal_position);
  2098.         ActuallySwapOrder(gOur_starting_position, gOriginal_position);
  2099.         ChallengeOccurred(gChallenger_index__racestrt, 0);
  2100.     }
  2101.     ChallengeOccurred(gChallenger_index__racestrt, pEscaped == 0);
  2102.     if (pTimed_out) {
  2103.         return 0;
  2104.     } else {
  2105.         return pCurrent_choice;
  2106.     }
  2107. }
  2108.  
  2109. // IDA: void __cdecl DoChallengeScreen()
  2110. void DoChallengeScreen(void) {
  2111.     static tFlicette flicker_on[2] = { { 43, { 54, 108 }, { 157, 377 } }, { 43, { 218, 436 }, { 157, 377 } } };
  2112.     static tFlicette flicker_off[2] = { { 42, { 54, 108 }, { 157, 377 } }, { 42, { 218, 436 }, { 157, 377 } } };
  2113.     static tFlicette push[2] = { { 304, { 54, 108 }, { 157, 377 } }, { 305, { 218, 436 }, { 157, 377 } } };
  2114.     static tMouse_area mouse_areas[2] = {
  2115.         { { 54, 108 }, { 157, 377 }, { 117, 234 }, { 178, 427 }, 0, 0, 0, NULL },
  2116.         { { 218, 436 }, { 157, 377 }, { 281, 562 }, { 178, 427 }, 1, 0, 0, NULL }
  2117.     };
  2118.     static tInterface_spec interface_spec = {
  2119.         0,               // initial_imode
  2120.         301,             // first_opening_flic
  2121.         0,               // second_opening_flic
  2122.         -1,              // end_flic_go_ahead
  2123.         -1,              // end_flic_escaped
  2124.         -1,              // end_flic_otherwise
  2125.         0,               // flic_bunch_to_load
  2126.         { -1, 0 },       // move_left_new_mode
  2127.         { -1, 0 },       // move_left_delta
  2128.         { 0, 0 },        // move_left_min
  2129.         { 1, 0 },        // move_left_max
  2130.         { NULL, NULL },  // move_left_proc
  2131.         { -1, 0 },       // move_right_new_mode
  2132.         { 1, 0 },        // move_right_delta
  2133.         { 0, 0 },        // move_right_min
  2134.         { 1, 0 },        // move_right_max
  2135.         { NULL, NULL },  // move_right_proc
  2136.         { -1, 0 },       // move_up_new_mode
  2137.         { 0, 0 },        // move_up_delta
  2138.         { 0, 0 },        // move_up_min
  2139.         { 0, 0 },        // move_up_max
  2140.         { NULL, NULL },  // move_up_proc
  2141.         { -1, 0 },       // move_down_new_mode
  2142.         { 0, 0 },        // move_down_delta
  2143.         { 0, 0 },        // move_down_min
  2144.         { 0, 0 },        // move_down_max
  2145.         { NULL, NULL },  // move_down_proc
  2146.         { 1, 1 },        // go_ahead_allowed
  2147.         { NULL, NULL },  // go_ahead_proc
  2148.         { 1, 1 },        // escape_allowed
  2149.         { NULL, NULL },  // escape_proc
  2150.         &CheckNextStage, // exit_proc
  2151.         &GridDraw,       // draw_proc
  2152.         0u,              // time_out
  2153.         NULL,            // start_proc1
  2154.         &ChallengeStart, // start_proc2
  2155.         &ChallengeDone,  // done_proc
  2156.         0,               // font_needed
  2157.         { 0, 0 },        // typeable
  2158.         NULL,            // get_original_string
  2159.         1,               // escape_code
  2160.         1,               // dont_save_or_load
  2161.         2,               // number_of_button_flics
  2162.         flicker_on,      // flicker_on_flics
  2163.         flicker_off,     // flicker_off_flics
  2164.         push,            // pushed_flics
  2165.         2,               // number_of_mouse_areas
  2166.         mouse_areas,     // mouse_areas
  2167.         0,               // number_of_recopy_areas
  2168.         NULL             // recopy_areas
  2169.     };
  2170.  
  2171.     //int result; // Pierre-Marie Baty -- unused variable
  2172.     LOG_TRACE("()");
  2173.  
  2174.     gOriginal_position = gOur_starting_position;
  2175.     gChallenger_position = IRandomBetween(0, 1);
  2176.     if (gOpponents[gCurrent_race.opponent_list[gChallenger_position].index].car_number < 0) {
  2177.         gChallenger_position ^= 1u;
  2178.     }
  2179.     gChallenger_index__racestrt = gCurrent_race.opponent_list[gChallenger_position].index;
  2180.     LoadOpponentMugShot(gChallenger_index__racestrt);
  2181.     gDraw_grid_status = eGrid_draw_none;
  2182.     gGrid_y_adjust = gCurrent_graf_data->dare_y_adjust;
  2183.     DoInterfaceScreen(&interface_spec, 0, 0);
  2184. }
  2185.  
  2186. // IDA: int __usercall GridDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
  2187. int GridDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
  2188.     LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
  2189.  
  2190.     if (pTimed_out) {
  2191.         return 0;
  2192.     }
  2193.     if (pEscaped) {
  2194.         return -1;
  2195.     }
  2196.     return pCurrent_choice;
  2197. }
  2198.  
  2199. // IDA: void __cdecl GridStart()
  2200. void GridStart(void) {
  2201.     LOG_TRACE("()");
  2202.  
  2203.     MoveGrid(-2, CalcGridOffset(gOur_starting_position), 400);
  2204.     PrintMemoryDump(0, "IN GRID SCREEN");
  2205. }
  2206.  
  2207. // IDA: int __usercall GridMoveLeft@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  2208. int GridMoveLeft(int* pCurrent_choice, int* pCurrent_mode) {
  2209.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  2210.  
  2211.     if (gOur_starting_position
  2212.         && gCurrent_race.opponent_list[gOur_starting_position - 1].ranking >= gProgram_state.rank) {
  2213.         AddToFlicQueue(
  2214.             gStart_interface_spec->pushed_flics[1].flic_index,
  2215.             gStart_interface_spec->pushed_flics[1].x[gGraf_data_index],
  2216.             gStart_interface_spec->pushed_flics[1].y[gGraf_data_index],
  2217.             1);
  2218.         DRS3StartSound(gEffects_outlet, 3000);
  2219.         DoGridTransition(gOur_starting_position, gOur_starting_position - 1);
  2220.     }
  2221.     return 0;
  2222. }
  2223.  
  2224. // IDA: int __usercall GridMoveRight@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  2225. int GridMoveRight(int* pCurrent_choice, int* pCurrent_mode) {
  2226.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  2227.  
  2228.     if (gOur_starting_position < gCurrent_race.number_of_racers - 1) {
  2229.         AddToFlicQueue(
  2230.             gStart_interface_spec->pushed_flics[2].flic_index,
  2231.             gStart_interface_spec->pushed_flics[2].x[gGraf_data_index],
  2232.             gStart_interface_spec->pushed_flics[2].y[gGraf_data_index],
  2233.             1);
  2234.         DRS3StartSound(gEffects_outlet, 3000);
  2235.         DoGridTransition(gOur_starting_position, gOur_starting_position + 1);
  2236.     }
  2237.     return 0;
  2238. }
  2239.  
  2240. // IDA: int __usercall GridClickCar@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  2241. int GridClickCar(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  2242.     int rel_pos;
  2243.     int new_pos;
  2244.     //int base_pos; // Pierre-Marie Baty -- unused variable
  2245.     int x_coord;
  2246.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  2247.  
  2248.     rel_pos = ((gCurrent_graf_data->grid_bottom_clip - gCurrent_graf_data->grid_top_clip) / 2) < pY_offset;
  2249.     if (rel_pos) {
  2250.         pX_offset -= gCurrent_graf_data->grid_x_stagger;
  2251.     }
  2252.     x_coord = pX_offset / gCurrent_graf_data->grid_x_pitch;
  2253.     if (x_coord > 2) {
  2254.         x_coord = 2;
  2255.     }
  2256.     new_pos = 2 * x_coord + rel_pos + (gOur_starting_position & ~1) - 2;
  2257.     if (new_pos >= 0 && new_pos < gCurrent_race.number_of_racers && gProgram_state.rank < gCurrent_race.opponent_list[new_pos].ranking) {
  2258.         DRS3StartSound(gEffects_outlet, 3000);
  2259.         DoGridTransition(gOur_starting_position, new_pos);
  2260.     }
  2261.     return 0;
  2262. }
  2263.  
  2264. // IDA: int __usercall GridClickNumbers@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  2265. int GridClickNumbers(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  2266.     int new_pos;
  2267.     int i;
  2268.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  2269.  
  2270.     new_pos = -1;
  2271.     for (i = 0; i < gCurrent_race.number_of_racers; i++) {
  2272.         if (gGrid_number_x_coords[i] <= pX_offset && (gCurrent_race.number_of_racers - 1 == i || pX_offset < gGrid_number_x_coords[i + 1])) {
  2273.             new_pos = i;
  2274.             break;
  2275.         }
  2276.     }
  2277.     if (new_pos >= 0 && new_pos < gCurrent_race.number_of_racers && gProgram_state.rank <= gCurrent_race.opponent_list[new_pos].ranking) {
  2278.         DRS3StartSound(gEffects_outlet, 3000);
  2279.         DoGridTransition(gOur_starting_position, new_pos);
  2280.     }
  2281.     return 0;
  2282. }
  2283.  
  2284. // IDA: int __usercall GridClickLeft@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  2285. int GridClickLeft(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  2286.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  2287.  
  2288.     GridMoveLeft(pCurrent_choice, pCurrent_mode);
  2289.     return 0;
  2290. }
  2291.  
  2292. // IDA: int __usercall GridClickRight@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  2293. int GridClickRight(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  2294.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  2295.  
  2296.     GridMoveRight(pCurrent_choice, pCurrent_mode);
  2297.     return 0;
  2298. }
  2299.  
  2300. // IDA: int __usercall CheckChallenge@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  2301. int CheckChallenge(int* pCurrent_choice, int* pCurrent_mode) {
  2302.     LOG_TRACE8("(%p, %p)", pCurrent_choice, pCurrent_mode);
  2303.  
  2304.     if (!gChallenge_time || PDGetTotalTime() < gChallenge_time) {
  2305.         return 0;
  2306.     }
  2307.     *pCurrent_choice = -2;
  2308.     return 1;
  2309. }
  2310.  
  2311. // IDA: int __usercall FindBestPos@<EAX>(int pOur_rank@<EAX>)
  2312. int FindBestPos(int pOur_rank) {
  2313.     int i;
  2314.     LOG_TRACE("(%d)", pOur_rank);
  2315.  
  2316.     for (i = gCurrent_race.number_of_racers - 1; i >= 0; i--) {
  2317.         if (gCurrent_race.opponent_list[i].ranking < pOur_rank) {
  2318.             return i + 1;
  2319.         }
  2320.     }
  2321.     return 0;
  2322. }
  2323.  
  2324. // IDA: int __usercall SortGridFunction@<EAX>(void *pFirst_one@<EAX>, void *pSecond_one@<EDX>)
  2325. int SortGridFunction(const void* pFirst_one, const void* pSecond_one) {
  2326.     LOG_TRACE("(%p, %p)", pFirst_one, pSecond_one);
  2327.  
  2328.     return ((tOpp_spec*)pFirst_one)->ranking - ((tOpp_spec*)pSecond_one)->ranking;
  2329. }
  2330.  
  2331. // IDA: void __cdecl SortOpponents()
  2332. void SortOpponents(void) {
  2333.     int i;
  2334.     LOG_TRACE("()");
  2335.  
  2336.     for (i = 0; gCurrent_race.number_of_racers > i; ++i) {
  2337.         if (gCurrent_race.opponent_list[i].index < 0) {
  2338.             return;
  2339.         }
  2340.     }
  2341.     qsort(gCurrent_race.opponent_list, gCurrent_race.number_of_racers, sizeof(tOpp_spec), SortGridFunction);
  2342.     gBest_pos_available = FindBestPos(gProgram_state.rank);
  2343.     gOur_starting_position = gCurrent_race.number_of_racers - 70 * (gCurrent_race.number_of_racers - gBest_pos_available) / 100;
  2344.     for (i = gCurrent_race.number_of_racers; i > gOur_starting_position; i--) {
  2345.         gCurrent_race.opponent_list[i] = gCurrent_race.opponent_list[i - 1];
  2346.     }
  2347.     gCurrent_race.opponent_list[gOur_starting_position].index = -1;
  2348.     gCurrent_race.opponent_list[gOur_starting_position].ranking = gProgram_state.rank;
  2349.     gCurrent_race.opponent_list[gOur_starting_position].car_spec = &gProgram_state.current_car;
  2350.     gCurrent_race.number_of_racers++;
  2351. }
  2352.  
  2353. // IDA: tSO_result __cdecl DoGridPosition()
  2354. tSO_result DoGridPosition(void) {
  2355.     static tFlicette flicker_on[3] = {
  2356.         { 43, { 240, 480 }, { 158, 379 } },
  2357.         { 293, { 56, 112 }, { 151, 362 } },
  2358.         { 296, { 136, 272 }, { 151, 362 } }
  2359.     };
  2360.  
  2361.     static tFlicette flicker_off[3] = {
  2362.         { 42, { 240, 480 }, { 158, 379 } },
  2363.         { 292, { 56, 112 }, { 151, 362 } },
  2364.         { 295, { 136, 272 }, { 151, 362 } }
  2365.     };
  2366.  
  2367.     static tFlicette push[3] = {
  2368.         { 154, { 240, 480 }, { 158, 379 } },
  2369.         { 294, { 56, 112 }, { 151, 362 } },
  2370.         { 297, { 136, 272 }, { 151, 362 } }
  2371.     };
  2372.     static tMouse_area mouse_areas[5] = {
  2373.         { { 240, 480 }, { 158, 379 }, { 305, 610 }, { 178, 427 }, 0, 0, 0, NULL },
  2374.         { { 56, 112 },
  2375.             { 151, 362 },
  2376.             { 91, 182 },
  2377.             { 158, 379 },
  2378.             -1,
  2379.             0,
  2380.             0,
  2381.             GridClickLeft },
  2382.         { { 136, 272 },
  2383.             { 151, 362 },
  2384.             { 171, 342 },
  2385.             { 158, 379 },
  2386.             -1,
  2387.             0,
  2388.             0,
  2389.             GridClickRight },
  2390.         { { 52, 104 },
  2391.             { 56, 134 },
  2392.             { 269, 538 },
  2393.             { 141, 338 },
  2394.             -1,
  2395.             0,
  2396.             0,
  2397.             GridClickCar },
  2398.         { { 60, 120 },
  2399.             { 163, 391 },
  2400.             { 235, 470 },
  2401.             { 176, 422 },
  2402.             -1,
  2403.             0,
  2404.             0,
  2405.             GridClickNumbers }
  2406.     };
  2407.  
  2408.     static tInterface_spec interface_spec = {
  2409.         0,                       // initial_imode
  2410.         290,                     // first_opening_flic
  2411.         0,                       // second_opening_flic
  2412.         0,                       // end_flic_go_ahead
  2413.         0,                       // end_flic_escaped
  2414.         0,                       // end_flic_otherwise
  2415.         8,                       // flic_bunch_to_load
  2416.         { -1, 0 },               // move_left_new_mode
  2417.         { 0, 0 },                // move_left_delta
  2418.         { 0, 0 },                // move_left_min
  2419.         { 0, 0 },                // move_left_max
  2420.         { GridMoveLeft, NULL },  // move_left_proc
  2421.         { -1, 0 },               // move_right_new_mode
  2422.         { 0, 0 },                // move_right_delta
  2423.         { 0, 0 },                // move_right_min
  2424.         { 0, 0 },                // move_right_max
  2425.         { GridMoveRight, NULL }, // move_right_proc
  2426.         { -1, 0 },               // move_up_new_mode
  2427.         { 0, 0 },                // move_up_delta
  2428.         { 0, 0 },                // move_up_min
  2429.         { 0, 0 },                // move_up_max
  2430.         { GridMoveLeft, NULL },  // move_up_proc
  2431.         { -1, 0 },               // move_down_new_mode
  2432.         { 0, 0 },                // move_down_delta
  2433.         { 0, 0 },                // move_down_min
  2434.         { 0, 0 },                // move_down_max
  2435.         { GridMoveRight, NULL }, // move_down_proc
  2436.         { 1, 1 },                // go_ahead_allowed
  2437.         { NULL, NULL },          // go_ahead_proc
  2438.         { 1, 1 },                // escape_allowed
  2439.         { NULL, NULL },          // escape_proc
  2440.         CheckChallenge,          // exit_proc
  2441.         GridDraw,                // draw_proc
  2442.         0u,                      // time_out
  2443.         NULL,                    // start_proc1
  2444.         GridStart,               // start_proc2
  2445.         GridDone,                // done_proc
  2446.         0,                       // font_needed
  2447.         { 0, 0 },                // typeable
  2448.         NULL,                    // get_original_string
  2449.         -1,                      // escape_code
  2450.         1,                       // dont_save_or_load
  2451.         3,                       // number_of_button_flics
  2452.         flicker_on,              // flicker_on_flics
  2453.         flicker_off,             // flicker_off_flics
  2454.         push,                    // pushed_flics
  2455.         5,                       // number_of_mouse_areas
  2456.         mouse_areas,             // mouse_areas
  2457.         0,                       // number_of_recopy_areas
  2458.         NULL                     // recopy_areas
  2459.     };
  2460.  
  2461.     int result;
  2462.     LOG_TRACE("()");
  2463.  
  2464.     gStart_interface_spec = &interface_spec;
  2465.     if (!gAusterity_mode) {
  2466.         LoadGridIcons(&gCurrent_race);
  2467.     }
  2468.     gSwap_grid_1 = -1;
  2469.     gSwap_grid_2 = -1;
  2470.     gDraw_grid_status = eGrid_draw_all;
  2471.     gGrid_y_adjust = 0;
  2472.     SortOpponents();
  2473.     if (!gAusterity_mode) {
  2474.         if (gBest_pos_available > 1
  2475.             && (gOpponents[gCurrent_race.opponent_list[0].index].car_number >= 0
  2476.                 || gOpponents[gCurrent_race.opponent_list[1].index].car_number >= 0)
  2477.             && PercentageChance(10)) {
  2478.             gChallenge_time = PDGetTotalTime() + IRandomBetween(1000, 10000);
  2479.         } else {
  2480.             gChallenge_time = 0;
  2481.         }
  2482.         result = DoInterfaceScreen(&interface_spec, 0, 0);
  2483.         if (result >= 0) {
  2484.             FadePaletteDown();
  2485.         } else {
  2486.             RunFlic(291);
  2487.         }
  2488.         if (result == -1) {
  2489.             return eSO_main_menu_invoked;
  2490.         }
  2491.         if (result == -2) {
  2492.             DoChallengeScreen();
  2493.         }
  2494.         DisposeGridIcons(&gCurrent_race);
  2495.     }
  2496.     return eSO_continue;
  2497. }
  2498.  
  2499. // IDA: void __cdecl CheckPlayersAreResponding()
  2500. void CheckPlayersAreResponding(void) {
  2501.     int i;
  2502.     tU32 time;
  2503.     tNet_message* message;
  2504.     LOG_TRACE("()");
  2505.  
  2506.     time = PDGetTotalTime();
  2507.     for (i = 0; i < gNumber_of_net_players; i++) {
  2508.         if (i != gThis_net_player_index && time - gNet_players[i].last_heard_from_him > 20000) {
  2509.             gNet_players[i].player_status = ePlayer_status_not_responding;
  2510.         }
  2511.     }
  2512.     if (gNet_mode == eNet_mode_client && gLast_host_query == 0) {
  2513.         message = NetBuildMessage(NETMSGID_HOSTQUERY, 0);
  2514.         NetGuaranteedSendMessageToHost(gCurrent_net_game, message, NULL);
  2515.         gLast_host_query = time;
  2516.     }
  2517. }
  2518.  
  2519. // IDA: void __cdecl NetSynchStartStart()
  2520. void NetSynchStartStart(void) {
  2521.     LOG_TRACE("()");
  2522.  
  2523.     CheckPlayersAreResponding();
  2524. }
  2525.  
  2526. // IDA: void __usercall DrawAnItem(int pX@<EAX>, int pY_index@<EDX>, int pFont_index@<EBX>, char *pText@<ECX>)
  2527. //  Suffix added to avoid duplicate symbol
  2528. void DrawAnItem__racestrt(int pX, int pY_index, int pFont_index, char* pText) {
  2529.     LOG_TRACE("(%d, %d, %d, \"%s\")", pX, pY_index, pFont_index, pText);
  2530.  
  2531.     TransBrPixelmapText(gBack_screen,
  2532.         pX,
  2533.         gCurrent_graf_data->start_synch_top + gCurrent_graf_data->start_synch_y_pitch * pY_index,
  2534.         pFont_index,
  2535.         gFont_7,
  2536.         pText);
  2537. }
  2538.  
  2539. // IDA: void __usercall NetSynchStartDraw(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
  2540. void NetSynchStartDraw(int pCurrent_choice, int pCurrent_mode) {
  2541.     int i;
  2542.     int number_ready;
  2543.     char s[256];
  2544.     LOG_TRACE("(%d, %d)", pCurrent_choice, pCurrent_mode);
  2545.  
  2546.     number_ready = 0;
  2547.     CheckPlayersAreResponding();
  2548.     NetPlayerStatusChanged(ePlayer_status_ready);
  2549.     for (i = 0; i < COUNT_OF(gNet_players); i++) {
  2550.         BrPixelmapRectangleFill(gBack_screen,
  2551.             gCurrent_graf_data->start_synch_x_0,
  2552.             gCurrent_graf_data->start_synch_y_pitch * i + gCurrent_graf_data->start_synch_top,
  2553.             gCurrent_graf_data->start_synch_x_r - gCurrent_graf_data->start_synch_x_1,
  2554.             gFont_7->glyph_y,
  2555.             0);
  2556.     }
  2557.     for (i = 0; i < gNumber_of_net_players; i++) {
  2558.         strcpy(s, gNet_players[i].player_name);
  2559.         if (gNet_players[i].host) {
  2560.             strcat(s, " -");
  2561.             strcat(s, GetMiscString(kMiscString_HOST));
  2562.             strcat(s, "-");
  2563.         }
  2564.         TurnOffPaletteConversion();
  2565.         DRPixelmapRectangleMaskedCopy(gBack_screen,
  2566.             gCurrent_graf_data->start_synch_x_0,
  2567.             gCurrent_graf_data->start_synch_top + 1 + gCurrent_graf_data->start_synch_y_pitch * i,
  2568.             gIcons_pix_low_res, /* DOS version uses low res, Windows version uses normal res */
  2569.             0,
  2570.             gNet_players[i].car_index * gCurrent_graf_data->net_head_icon_height,
  2571.             gIcons_pix_low_res->width, /* DOS version uses low res, Windows version uses normal res */
  2572.             gCurrent_graf_data->net_head_icon_height);
  2573.         TurnOnPaletteConversion();
  2574.         DrawAnItem__racestrt(
  2575.             gCurrent_graf_data->start_synch_x_1,
  2576.             i,
  2577.             (gNet_players[i].player_status == ePlayer_status_ready) ? 66 : 4,
  2578.             s);
  2579.         DrawAnItem__racestrt(gCurrent_graf_data->start_synch_x_2,
  2580.             i,
  2581.             (gNet_players[i].player_status == ePlayer_status_ready) ? 83 : ((gNet_players[i].player_status == ePlayer_status_not_responding) ? 247 : 4),
  2582.             GetMiscString(kMiscString_NetworkPlayerStatus_START + gNet_players[i].player_status));
  2583.         if (gNet_players[i].player_status == ePlayer_status_ready) {
  2584.             number_ready++;
  2585.         }
  2586.     }
  2587.     if (gNet_mode == eNet_mode_host && gNumber_of_net_players == number_ready && gNumber_of_net_players > 1 && (!gNo_races_yet || gNumber_of_net_players == 6)) {
  2588.         SignalToStartRace();
  2589.         gSynch_race_start = 1;
  2590.     }
  2591. }
  2592.  
  2593. // IDA: int __usercall NetSynchStartDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
  2594. int NetSynchStartDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
  2595.     LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
  2596.  
  2597.     if (pEscaped) {
  2598.         pCurrent_choice = -1;
  2599.     } else if (pCurrent_choice == 0) {
  2600.         gProgram_state.prog_status = eProg_idling;
  2601.     }
  2602.     return pCurrent_choice;
  2603. }
  2604.  
  2605. // IDA: int __usercall NetSynchStartGoAhead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  2606. int NetSynchStartGoAhead(int* pCurrent_choice, int* pCurrent_mode) {
  2607.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  2608.  
  2609.     if (*pCurrent_choice == 0 || (gNet_mode == eNet_mode_host && *pCurrent_choice >= 0)) {
  2610.         if (*pCurrent_choice == 0) {
  2611.             gProgram_state.prog_status = eProg_idling;
  2612.             return 1;
  2613.         } else if (gNet_mode == eNet_mode_host && *pCurrent_choice == 1) {
  2614.             if (gNumber_of_net_players <= 1) {
  2615.                 DRS3StartSound(gEffects_outlet, 3100);
  2616.                 return 0;
  2617.             } else {
  2618.                 SignalToStartRace();
  2619.                 gSynch_race_start = 1;
  2620.                 gNo_races_yet = 0;
  2621.                 return 1;
  2622.             }
  2623.         } else {
  2624.             return 1;
  2625.         }
  2626.     } else {
  2627.         DRS3StartSound(gEffects_outlet, 3100);
  2628.         return 0;
  2629.     }
  2630. }
  2631.  
  2632. // IDA: int __usercall ExitWhenReady@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  2633. int ExitWhenReady(int* pCurrent_choice, int* pCurrent_mode) {
  2634.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  2635.  
  2636.     if (!gSynch_race_start && gProgram_state.prog_status != eProg_game_starting) {
  2637.         if (gProgram_state.prog_status == eProg_idling) {
  2638.             *pCurrent_choice = 0;
  2639.             return 1;
  2640.         } else {
  2641.             return 0;
  2642.         }
  2643.     } else {
  2644.         *pCurrent_choice = 1;
  2645.         return 1;
  2646.     }
  2647. }
  2648.  
  2649. // IDA: tSO_result __usercall NetSynchRaceStart2@<EAX>(tNet_synch_mode pMode@<EAX>)
  2650. tSO_result NetSynchRaceStart2(tNet_synch_mode pMode) {
  2651.     static tFlicette flicker_on_hf[2] = {
  2652.         { 321, { 219, 112 }, { 172, 362 } },
  2653.         { 321, { 39, 480 }, { 172, 379 } },
  2654.     };
  2655.     static tFlicette flicker_off_hf[2] = {
  2656.         { 322, { 219, 112 }, { 172, 362 } },
  2657.         { 322, { 39, 480 }, { 172, 379 } },
  2658.     };
  2659.     static tFlicette push_hf[2] = {
  2660.         { 206, { 219, 112 }, { 172, 362 } },
  2661.         { 205, { 39, 480 }, { 172, 379 } },
  2662.     };
  2663.     static tMouse_area mouse_areas_hf[2] = {
  2664.         { { 219, 480 }, { 172, 379 }, { 282, 182 }, { 192, 427 }, 0, 0, 0, NULL },
  2665.         { { 39, 112 }, { 172, 362 }, { 102, 182 }, { 192, 379 }, 1, 0, 0, NULL },
  2666.     };
  2667.     static tInterface_spec interface_spec_hf = {
  2668.         0,
  2669.         203,
  2670.         0,
  2671.         0,
  2672.         0,
  2673.         0,
  2674.         8,
  2675.         { -1, 0 },
  2676.         { 1, 0 },
  2677.         { 0, 0 },
  2678.         { 1, 0 },
  2679.         { NULL, NULL },
  2680.         { -1, 0 },
  2681.         { 1, 0 },
  2682.         { 0, 0 },
  2683.         { 1, 0 },
  2684.         { NULL, NULL },
  2685.         { -1, 0 },
  2686.         { 1, 0 },
  2687.         { 0, 0 },
  2688.         { 1, 0 },
  2689.         { NULL, NULL },
  2690.         { -1, 0 },
  2691.         { 1, 0 },
  2692.         { 0, 0 },
  2693.         { 1, 0 },
  2694.         { NULL, NULL },
  2695.         { 1, 1 },
  2696.         { NetSynchStartGoAhead, NetSynchStartGoAhead },
  2697.         { 1, 1 },
  2698.         { NULL, NULL },
  2699.         ExitWhenReady,
  2700.         NetSynchStartDraw,
  2701.         0,
  2702.         NULL,
  2703.         NetSynchStartStart,
  2704.         NetSynchStartDone,
  2705.         0,
  2706.         { 0, 0 },
  2707.         NULL,
  2708.         -1,
  2709.         1,
  2710.         COUNT_OF(flicker_on_hf),
  2711.         flicker_on_hf,
  2712.         flicker_off_hf,
  2713.         push_hf,
  2714.         COUNT_OF(mouse_areas_hf),
  2715.         mouse_areas_hf,
  2716.         0,
  2717.         NULL,
  2718.     };
  2719.     static tFlicette flicker_on_hs[1] = {
  2720.         { 321, { 219, 112 }, { 172, 362 } },
  2721.     };
  2722.     static tFlicette flicker_off_hs[1] = {
  2723.         { 322, { 219, 112 }, { 172, 362 } },
  2724.     };
  2725.     static tFlicette push_hs[1] = {
  2726.         { 206, { 219, 112 }, { 172, 362 } },
  2727.     };
  2728.     static tMouse_area mouse_areas_hs[1] = {
  2729.         { { 219, 480 }, { 172, 379 }, { 282, 182 }, { 192, 427 }, 0, 0, 0, NULL },
  2730.     };
  2731.     static tInterface_spec interface_spec_hs = {
  2732.         0, 209, 0, 0, 0, 0, 8,
  2733.         { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
  2734.         { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
  2735.         { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
  2736.         { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
  2737.         { 1, 1 }, { NetSynchStartGoAhead, NetSynchStartGoAhead }, { 1, 1 }, { NULL, NULL },
  2738.         ExitWhenReady, NetSynchStartDraw, 0, NULL, NetSynchStartStart, NetSynchStartDone, 0, { 0, 0 },
  2739.         NULL, -1, 1,
  2740.         COUNT_OF(flicker_on_hs), flicker_on_hs, flicker_off_hs, push_hs,
  2741.         COUNT_OF(mouse_areas_hs), mouse_areas_hs, 0, NULL
  2742.     };
  2743.     static tFlicette flicker_on_c[1] = {
  2744.         { 321, { 219, 112 }, { 172, 362 } },
  2745.     };
  2746.     static tFlicette flicker_off_c[1] = {
  2747.         { 322, { 219, 112 }, { 172, 362 } },
  2748.     };
  2749.     static tFlicette push_c[1] = {
  2750.         { 207, { 219, 112 }, { 172, 362 } },
  2751.     };
  2752.     static tMouse_area mouse_areas_c[1] = {
  2753.         { { 219, 112 }, { 172, 362 }, { 282, 182 }, { 192, 379 }, 0, 0, 0, NULL },
  2754.     };
  2755.     static tInterface_spec interface_spec_c = {
  2756.         0, 204, 0, 0, 0, 0, 8,
  2757.         { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
  2758.         { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
  2759.         { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
  2760.         { -1, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }, { NULL, NULL },
  2761.         { 1, 1 }, { NetSynchStartGoAhead, NetSynchStartGoAhead }, { 1, 1 }, { NULL, NULL },
  2762.         ExitWhenReady, NetSynchStartDraw, 0, NULL, NetSynchStartStart, NetSynchStartDone, 0, { 0, 0 },
  2763.         NULL, -1, 1,
  2764.         COUNT_OF(flicker_on_c), flicker_on_c, flicker_off_c, push_c,
  2765.         COUNT_OF(mouse_areas_c), mouse_areas_c, 0, NULL
  2766.     };
  2767.     int result;
  2768.     LOG_TRACE("(%d)", pMode);
  2769.  
  2770.     if (pMode != eNet_synch_client) {
  2771.         if (gCurrent_net_game->status.stage == eNet_game_starting) {
  2772.             gCurrent_net_game->status.stage = eNet_game_ready;
  2773.         }
  2774.         SetUpNetCarPositions();
  2775.         // gNet_synch_start = PDGetTotalTime();
  2776.     }
  2777.     TurnOnPaletteConversion();
  2778.     switch (pMode) {
  2779.     case eNet_synch_host_first:
  2780.         result = DoInterfaceScreen(&interface_spec_hf, 0, 1);
  2781.         break;
  2782.     case eNet_synch_host_subsequent:
  2783.         result = DoInterfaceScreen(&interface_spec_hs, 0, -1);
  2784.         break;
  2785.     case eNet_synch_client:
  2786.         result = DoInterfaceScreen(&interface_spec_c, 0, -1);
  2787.         break;
  2788.     default:
  2789.         break;
  2790.     }
  2791.     TurnOffPaletteConversion();
  2792.     FadePaletteDown();
  2793.     if (result > -2 && result < 1) {
  2794.         NetLeaveGame(gCurrent_net_game);
  2795.     }
  2796.     return eSO_continue;
  2797. }
  2798.  
  2799. // IDA: tSO_result __cdecl NetSynchRaceStart()
  2800. tSO_result NetSynchRaceStart(void) {
  2801.     LOG_TRACE("()");
  2802.  
  2803.     SuspendPendingFlic();
  2804.     if (gNet_mode == eNet_mode_host) {
  2805.         if (gNo_races_yet) {
  2806.             return NetSynchRaceStart2(eNet_synch_host_first);
  2807.         } else {
  2808.             return NetSynchRaceStart2(eNet_synch_host_subsequent);
  2809.         }
  2810.     } else {
  2811.         return NetSynchRaceStart2(eNet_synch_client);
  2812.     }
  2813. }
  2814.