Subversion Repositories Games.Carmageddon

Rev

Rev 11 | Rev 20 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include "network.h"
  2. #include "brender.h"
  3. #include "car.h"
  4. #include "controls.h"
  5. #include "displays.h"
  6. #include "errors.h"
  7. #include "globvars.h"
  8. #include "globvrpb.h"
  9. #include "graphics.h"
  10. #include "harness/hooks.h"
  11. #include "harness/trace.h"
  12. #include "loading.h"
  13. #include "netgame.h"
  14. #include "newgame.h"
  15. #include "oil.h"
  16. #include "opponent.h"
  17. #include "pd/net.h"
  18. #include "pd/sys.h"
  19. #include "pedestrn.h"
  20. #include "piping.h"
  21. #include "powerup.h"
  22. #include "pratcam.h"
  23. #include "replay.h"
  24. #include "spark.h"
  25. #include "structur.h"
  26. #include "utility.h"
  27. #include "world.h"
  28. #include <stdlib.h>
  29.  
  30. tU32 gMess_max_flags;
  31. tU32 gMess_mid_flags;
  32. tU32 gMess_min_flags;
  33. tU32 gGuarantee_number;
  34. int gNet_service_disable = 0;
  35. int gIn_net_service = 0;
  36. int gPlayer_list_batch_number = 0;
  37. int gOnly_receive_guarantee_replies = 0;
  38. void* gMessage_to_free;
  39. tNet_message* gBroadcast_stack;
  40. tNet_message* gTo_host_stack;
  41. tU32 gLast_flush_message = 0;
  42. int gRace_only_flags[33] = {
  43.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
  44.     1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
  45.     0
  46. };
  47. int gJoin_list_mode;
  48. tNet_game_player_info gNew_net_players[6];
  49. tGuaranteed_message gGuarantee_list[100]; // DOS debug symbols has this as [150]
  50. tMid_message* gMid_messages;
  51. tU32 gLast_player_list_received;
  52. tMin_message* gMin_messages;
  53. void (*gAdd_proc)(tNet_game_details*);
  54. int gReceiving_batch_number;
  55. int gReceiving_new_players;
  56. tMax_message* gMax_messages;
  57. int gNext_guarantee;
  58. tU32 gAsk_time;
  59. int gNet_initialised;
  60. int gDont_allow_joiners;
  61. tNet_game_details* gCurrent_join_poll_game;
  62. int gMessage_header_size;
  63. int gJoin_poll_index;
  64. int gJoin_request_denied;
  65. int gHost_died;
  66. int gCar_was_taken;
  67. int gBastard_has_answered;
  68. int gTime_for_next_one;
  69. int gReceived_game_scores;
  70.  
  71. #define MIN_MESSAGES_CAPACITY 20
  72. #define MID_MESSAGES_CAPACITY 10
  73. #define MAX_MESSAGES_CAPACITY 20
  74.  
  75. #define MAX_MESAGE_STACK_SIZE 512
  76.  
  77. // IDA: int __cdecl NetInitialise()
  78. int NetInitialise(void) {
  79.     int i;
  80.     LOG_TRACE("()");
  81.  
  82.     SwitchToRealResolution();
  83.     InitAbuseomatic();
  84.     gNet_service_disable = 0;
  85.     gIn_net_service = 0;
  86.     gMessage_header_size = PDNetGetHeaderSize();
  87.     gOnly_receive_guarantee_replies = 0;
  88.     gMin_messages = BrMemAllocate(MIN_MESSAGES_CAPACITY * (gMessage_header_size + sizeof(tMin_message)), kMem_net_min_messages);
  89.     gMid_messages = BrMemAllocate(MID_MESSAGES_CAPACITY * (gMessage_header_size + sizeof(tMid_message)), kMem_net_mid_messages);
  90.     gMax_messages = BrMemAllocate(MAX_MESSAGES_CAPACITY * (gMessage_header_size + sizeof(tMax_message)), kMem_net_max_messages);
  91.     for (i = 0; i < MIN_MESSAGES_CAPACITY; i++) {
  92.         ((tNet_message*)&gMin_messages[i])->contents.header.type = NETMSGID_NONE;
  93.     }
  94.     for (i = 0; i < MID_MESSAGES_CAPACITY; i++) {
  95.         ((tNet_message*)&gMid_messages[i])->contents.header.type = NETMSGID_NONE;
  96.     }
  97.     for (i = 0; i < MAX_MESSAGES_CAPACITY; i++) {
  98.         ((tNet_message*)&gMax_messages[i])->contents.header.type = NETMSGID_NONE;
  99.     }
  100.     gNet_initialised = PDNetInitialise() == 0;
  101.     if (gNet_initialised) {
  102.         InitNetHeadups();
  103.     }
  104.     GenerateItFoxShadeTable();
  105.     gDont_allow_joiners = 0;
  106.     SwitchToLoresMode();
  107.     return !gNet_initialised;
  108. }
  109.  
  110. // IDA: int __cdecl NetShutdown()
  111. int NetShutdown(void) {
  112.     int err;
  113.     //int i; // Pierre-Marie Baty -- unused variable
  114.     LOG_TRACE("()");
  115.  
  116.     err = PDNetShutdown();
  117.     DisposeAbuseomatic();
  118.     BrMemFree(gMin_messages);
  119.     BrMemFree(gMid_messages);
  120.     BrMemFree(gMax_messages);
  121.     DisposeNetHeadups();
  122.     return err;
  123. }
  124.  
  125. // IDA: void __cdecl ShutdownNetIfRequired()
  126. void ShutdownNetIfRequired(void) {
  127.     LOG_TRACE("()");
  128.  
  129.     if (gNet_initialised) {
  130.         NetShutdown();
  131.         gNet_initialised = 0;
  132.     }
  133. }
  134.  
  135. // IDA: void __cdecl DisableNetService()
  136. void DisableNetService(void) {
  137.     LOG_TRACE("()");
  138.  
  139.     gNet_service_disable = 1;
  140. }
  141.  
  142. // IDA: void __cdecl ReenableNetService()
  143. void ReenableNetService(void) {
  144.     LOG_TRACE("()");
  145.  
  146.     gNet_service_disable = 0;
  147. }
  148.  
  149. // IDA: int __cdecl PermitNetServiceReentrancy()
  150. int PermitNetServiceReentrancy(void) {
  151.     int prev;
  152.     LOG_TRACE("()");
  153.  
  154.     prev = !!gIn_net_service;
  155.     if (prev) {
  156.         gIn_net_service = 0;
  157.     }
  158.     return prev;
  159. }
  160.  
  161. // IDA: void __cdecl HaltNetServiceReentrancy()
  162. void HaltNetServiceReentrancy(void) {
  163.     LOG_TRACE("()");
  164.  
  165.     gIn_net_service = 1;
  166. }
  167.  
  168. // IDA: void __usercall NetSendHeadupToAllPlayers(char *pMessage@<EAX>)
  169. void NetSendHeadupToAllPlayers(char* pMessage) {
  170.     tNet_contents* the_contents;
  171.     LOG_TRACE("(\"%s\")", pMessage);
  172.  
  173.     if (gNet_mode) {
  174.         the_contents = NetGetBroadcastContents(NETMSGID_HEADUP, 0);
  175.         strcpy(the_contents->data.headup.text, pMessage);
  176.     }
  177. }
  178.  
  179. // IDA: void __usercall NetSendHeadupToEverybody(char *pMessage@<EAX>)
  180. void NetSendHeadupToEverybody(char* pMessage) {
  181.     tNet_contents* the_contents;
  182.     LOG_TRACE("(\"%s\")", pMessage);
  183.  
  184.     if (gNet_mode == eNet_mode_none) {
  185.         return;
  186.     }
  187.     if (gProgram_state.racing) {
  188.         NewTextHeadupSlot(4, 0, 3000, -4, pMessage);
  189.     }
  190.     the_contents = NetGetBroadcastContents(NETMSGID_HEADUP, 0);
  191.     strcpy(the_contents->data.headup.text, pMessage);
  192. }
  193.  
  194. // IDA: void __usercall NetSendHeadupToPlayer(char *pMessage@<EAX>, tPlayer_ID pPlayer@<EDX>)
  195. void NetSendHeadupToPlayer(char* pMessage, tPlayer_ID pPlayer) {
  196.     tNet_message* message;
  197.     LOG_TRACE("(\"%s\", %d)", pMessage, pPlayer);
  198.  
  199.     if (gNet_mode == eNet_mode_none) {
  200.         return;
  201.     }
  202.     if (gLocal_net_ID == pPlayer) {
  203.         if (gProgram_state.racing) {
  204.             NewTextHeadupSlot(4, 0, 3000, -4, pMessage);
  205.         }
  206.     } else {
  207.         message = NetBuildMessage(NETMSGID_HEADUP, 0);
  208.         strcpy(message->contents.data.headup.text, pMessage);
  209.         NetGuaranteedSendMessageToPlayer(gCurrent_net_game, message, pPlayer, 0);
  210.     }
  211. }
  212.  
  213. // IDA: void __cdecl InitialisePlayerStati()
  214. void InitialisePlayerStati(void) {
  215.     int i;
  216.     LOG_TRACE("()");
  217.  
  218.     for (i = 0; i < COUNT_OF(gNet_players); i++) {
  219.         gNet_players[i].last_heard_from_him = PDGetTotalTime();
  220.         gNet_players[i].player_status = ePlayer_status_loading;
  221.     }
  222. }
  223.  
  224. // IDA: void __cdecl LeaveTempGame()
  225. void LeaveTempGame(void) {
  226.     LOG_TRACE("()");
  227.  
  228.     if (gCurrent_join_poll_game != NULL) {
  229.         NetLeaveGameLowLevel(gCurrent_join_poll_game);
  230.     }
  231.     gTime_for_next_one = 1;
  232.     gCurrent_join_poll_game = NULL;
  233. }
  234.  
  235. // IDA: void __cdecl DisposeCurrentJoinPollGame()
  236. void DisposeCurrentJoinPollGame(void) {
  237.     LOG_TRACE("()");
  238.  
  239.     if (gCurrent_join_poll_game != NULL) {
  240.         NetDisposeGameDetails(gCurrent_join_poll_game);
  241.         gCurrent_join_poll_game = NULL;
  242.     }
  243. }
  244.  
  245. // IDA: void __cdecl DoNextJoinPoll()
  246. void DoNextJoinPoll(void) {
  247.     tNet_message* the_message;
  248.     LOG_TRACE("()");
  249.  
  250.     if (gTime_for_next_one) {
  251.         gCurrent_join_poll_game = NetAllocatePIDGameDetails();
  252.         if (gCurrent_join_poll_game != NULL) {
  253.             if (PDNetGetNextJoinGame(gCurrent_join_poll_game, gJoin_poll_index)) {
  254.                 if (NetJoinGameLowLevel(gCurrent_join_poll_game, "!TEMP!")) {
  255.                     DisposeCurrentJoinPollGame();
  256.                 } else {
  257.                     gTime_for_next_one = 0;
  258.                     the_message = NetBuildMessage(NETMSGID_SENDMEDETAILS, 0);
  259.                     NetSendMessageToAddress(gCurrent_join_poll_game, the_message, gCurrent_join_poll_game);
  260.                     gBastard_has_answered = 0;
  261.                     gAsk_time = PDGetTotalTime();
  262.                 }
  263.                 gJoin_poll_index++;
  264.             } else {
  265.                 gJoin_poll_index = 0;
  266.                 if (gCurrent_join_poll_game != NULL) {
  267.                     DisposeCurrentJoinPollGame();
  268.                 }
  269.             }
  270.         }
  271.     } else {
  272.         if (gBastard_has_answered) {
  273.             gAdd_proc(gCurrent_join_poll_game);
  274.             LeaveTempGame();
  275.         } else {
  276.             if (PDGetTotalTime() - gAsk_time > 10000) {
  277.                 LeaveTempGame();
  278.                 DisposeCurrentJoinPollGame();
  279.             }
  280.         }
  281.     }
  282. }
  283.  
  284. // IDA: void __usercall NetStartProducingJoinList(void (*pAdd_proc)(tNet_game_details*)@<EAX>)
  285. void NetStartProducingJoinList(void (*pAdd_proc)(tNet_game_details*)) {
  286.     LOG_TRACE("(%p)", pAdd_proc);
  287.  
  288.     gAdd_proc = pAdd_proc;
  289.     gJoin_list_mode = 1;
  290.     gBastard_has_answered = 0;
  291.     gTime_for_next_one = 1;
  292.     gJoin_poll_index = 0;
  293.     gCurrent_join_poll_game = NULL;
  294.     PDNetStartProducingJoinList();
  295. }
  296.  
  297. // IDA: void __cdecl NetEndJoinList()
  298. void NetEndJoinList(void) {
  299.     LOG_TRACE("()");
  300.  
  301.     gJoin_list_mode = 0;
  302.     DisposeCurrentJoinPollGame();
  303.     LeaveTempGame();
  304.     PDNetEndJoinList();
  305. }
  306.  
  307. // IDA: void __usercall NetDisposePIDGameInfo(tNet_game_details *pDetails@<EAX>)
  308. void NetDisposePIDGameInfo(tNet_game_details* pDetails) {
  309.     LOG_TRACE("(%p)", pDetails);
  310.  
  311.     if (pDetails != NULL) {
  312.         BrMemFree(pDetails);
  313.     }
  314. }
  315.  
  316. // IDA: void __usercall NetDisposeGameDetails(tNet_game_details *pDetails@<EAX>)
  317. void NetDisposeGameDetails(tNet_game_details* pDetails) {
  318.     LOG_TRACE("(%p)", pDetails);
  319.  
  320.     // LOG_WARN("NetDisposeGameDetails(%p)", pDetails);
  321.     if (pDetails != NULL) {
  322.         NetDisposePIDGameInfo(pDetails);
  323.     }
  324. }
  325.  
  326. // IDA: tNet_game_details* __cdecl NetAllocatePIDGameDetails()
  327. tNet_game_details* NetAllocatePIDGameDetails(void) {
  328.     //tNet_game_details* game; // Pierre-Marie Baty -- unused variable
  329.     LOG_TRACE("()");
  330.  
  331.     return BrMemAllocate(sizeof(tNet_game_details), kMem_net_pid_details);
  332. }
  333.  
  334. // IDA: void __usercall NetLeaveGameLowLevel(tNet_game_details *pDetails@<EAX>)
  335. void NetLeaveGameLowLevel(tNet_game_details* pDetails) {
  336.     LOG_TRACE("(%p)", pDetails);
  337.  
  338.     if (gNet_mode == eNet_mode_host) {
  339.         PDNetHostFinishGame(gCurrent_net_game);
  340.     } else {
  341.         PDNetLeaveGame(pDetails);
  342.     }
  343. }
  344.  
  345. // IDA: void __usercall NetLeaveGame(tNet_game_details *pNet_game@<EAX>)
  346. void NetLeaveGame(tNet_game_details* pNet_game) {
  347.     tNet_message* the_message;
  348.     char s[256];
  349.     //char* s2; // Pierre-Marie Baty -- unused variable
  350.     int i;
  351.     int must_revert_reentrancy;
  352.     LOG_TRACE("(%p)", pNet_game);
  353.  
  354.     if (gNet_mode == eNet_mode_none) {
  355.         return;
  356.     }
  357.     gOnly_receive_guarantee_replies = 1;
  358.     if (gNet_mode == eNet_mode_host) {
  359.         gDont_allow_joiners = 1;
  360.         the_message = NetBuildMessage(NETMSGID_HOSTICIDE, 0);
  361.         must_revert_reentrancy = PermitNetServiceReentrancy();
  362.         NetGuaranteedSendMessageToAllPlayers(pNet_game, the_message, NULL);
  363.         if (must_revert_reentrancy) {
  364.             HaltNetServiceReentrancy();
  365.         }
  366.     } else if (!gHost_died) {
  367.         the_message = NetBuildMessage(NETMSGID_LEAVE, 0);
  368.         NetGuaranteedSendMessageToHost(pNet_game, the_message, NULL);
  369.         strcpy(s, gProgram_state.player_name[0]);
  370.         strcat(s, " ");
  371.         strcat(s, GetMiscString(kMiscString_HasLeftTheGame));
  372.         NetSendHeadupToAllPlayers(s);
  373.     }
  374.     for (i = 0; i < gNumber_of_net_players; i++) {
  375.         DisposeCarN(i);
  376.     }
  377.     ClearOutStorageSpace(&gOur_car_storage_space);
  378.     ClearOutStorageSpace(&gNet_cars_storage_space);
  379.     must_revert_reentrancy = PermitNetServiceReentrancy();
  380.     NetSendMessageStacks();
  381.     NetWaitForGuaranteeReplies();
  382.     NetLeaveGameLowLevel(gCurrent_net_game);
  383.     if (must_revert_reentrancy) {
  384.         HaltNetServiceReentrancy();
  385.     }
  386.     gCurrent_net_game = NULL;
  387.     gNet_mode = eNet_mode_none;
  388.     gNumber_of_net_players = 0;
  389.     gProgram_state.prog_status = eProg_idling;
  390.     gOnly_receive_guarantee_replies = 0;
  391. }
  392.  
  393. // IDA: void __usercall NetSetPlayerSystemInfo(tNet_game_player_info *pPlayer@<EAX>, void *pSender_address@<EDX>)
  394. void NetSetPlayerSystemInfo(tNet_game_player_info* pPlayer, void* pSender_address) {
  395.     LOG_TRACE("(%p, %p)", pPlayer, pSender_address);
  396.  
  397.     PDNetSetPlayerSystemInfo(pPlayer, pSender_address);
  398. }
  399.  
  400. // IDA: void __usercall NetDisposePlayer(tNet_game_player_info *pPlayer@<EAX>)
  401. void NetDisposePlayer(tNet_game_player_info* pPlayer) {
  402.     LOG_TRACE("(%p)", pPlayer);
  403.  
  404.     PDNetDisposePlayer(pPlayer);
  405. }
  406.  
  407. // IDA: void __usercall FillInThisPlayer(tNet_game_details *pGame@<EAX>, tNet_game_player_info *pPlayer@<EDX>, int pCar_index@<EBX>, int pHost@<ECX>)
  408. void FillInThisPlayer(tNet_game_details* pGame, tNet_game_player_info* pPlayer, int pCar_index, int pHost) {
  409.     LOG_TRACE("(%p, %p, %d, %d)", pGame, pPlayer, pCar_index, pHost);
  410.  
  411.     pPlayer->host = pHost;
  412.     pPlayer->ID = NetExtractPlayerID(pGame);
  413.     strcpy(pPlayer->player_name, gProgram_state.player_name[0]);
  414.     pPlayer->this_players_time_stamp = PDGetTotalTime();
  415.     pPlayer->car_index = pCar_index;
  416.     pPlayer->reposition_time = 0;
  417.     pPlayer->last_waste_message = 0;
  418.     pPlayer->wasteage_attributed = 0;
  419.     pPlayer->name_not_clipped = 1;
  420.     pPlayer->played = 0;
  421.     pPlayer->won = 0;
  422.     pPlayer->games_score = 0;
  423.     pPlayer->race_stuff_initialised = 0;
  424.     if (pGame->options.random_car_choice && (pGame->options.car_choice == eNet_car_all || pGame->options.car_choice == eNet_car_both)) {
  425.         pPlayer->next_car_index = -1;
  426.     }
  427.     InitialisePlayerScore(pPlayer);
  428. }
  429.  
  430. // IDA: void __usercall LoadCarN(int pIndex@<EAX>, tNet_game_player_info *pPlayer@<EDX>)
  431. void LoadCarN(int pIndex, tNet_game_player_info* pPlayer) {
  432.     int switched_res;
  433.     LOG_TRACE("(%d, %p)", pIndex, pPlayer);
  434.  
  435.     pPlayer->car = BrMemAllocate(sizeof(tCar_spec), kMem_net_car_spec);
  436.     switched_res = SwitchToRealResolution();
  437.     LoadCar(gOpponents[pPlayer->car_index].car_file_name,
  438.         eDriver_net_human,
  439.         pPlayer->car,
  440.         pPlayer->car_index,
  441.         pPlayer->player_name,
  442.         &gNet_cars_storage_space);
  443.     if (switched_res) {
  444.         SwitchToLoresMode();
  445.     }
  446.     InitialiseCar(pPlayer->car);
  447.     if (pPlayer->player_status < ePlayer_status_racing) {
  448.         pPlayer->car->disabled = 1;
  449.     }
  450.     SetCarStorageTexturingLevel(&gNet_cars_storage_space, GetCarTexturingLevel(), eCTL_full);
  451. }
  452.  
  453. // IDA: void __usercall DisposeCarN(int pIndex@<EAX>)
  454. void DisposeCarN(int pIndex) {
  455.     int i;
  456.     int j;
  457.     LOG_TRACE("(%d)", pIndex);
  458.  
  459.     for (i = 0; i < gCurrent_race.number_of_racers; i++) {
  460.         if (gCurrent_race.opponent_list[i].car_spec == gNet_players[pIndex].car) {
  461.             gCurrent_race.number_of_racers--;
  462.             for (j = i; j < gCurrent_race.number_of_racers; j++) {
  463.                 gCurrent_race.opponent_list[j].index = gCurrent_race.opponent_list[j + 1].index;
  464.                 gCurrent_race.opponent_list[j].ranking = gCurrent_race.opponent_list[j + 1].ranking;
  465.                 gCurrent_race.opponent_list[j].net_player_index = gCurrent_race.opponent_list[j + 1].net_player_index;
  466.                 gCurrent_race.opponent_list[j].car_spec = gCurrent_race.opponent_list[j + 1].car_spec;
  467.             }
  468.         }
  469.     }
  470.     if (gProgram_state.racing) {
  471.         DisposeKevStuffCar(gNet_players[pIndex].car);
  472.     }
  473.     DisposeCar(gNet_players[pIndex].car, gNet_players[pIndex].car_index);
  474.     if (gThis_net_player_index != pIndex) {
  475.         BrMemFree(gNet_players[pIndex].car);
  476.     }
  477.     if (gAction_replay_mode) {
  478.         ToggleReplay();
  479.     }
  480.     ResetPiping();
  481. }
  482.  
  483. // IDA: void __usercall PlayerHasLeft(int pIndex@<EAX>)
  484. void PlayerHasLeft(int pIndex) {
  485.     LOG_TRACE("(%d)", pIndex);
  486.  
  487.     if (gCurrent_net_game->options.random_car_choice && (gCurrent_net_game->options.car_choice == eNet_car_all || gCurrent_net_game->options.car_choice == eNet_car_both)) {
  488.         if (gNet_players[pIndex].car_index >= 0) {
  489.             gCar_details[gNet_players[pIndex].car_index].ownership = eCar_owner_none;
  490.         }
  491.         if (gNet_players[pIndex].next_car_index >= 0) {
  492.             gCar_details[gNet_players[pIndex].next_car_index].ownership = eCar_owner_none;
  493.         }
  494.     }
  495. }
  496.  
  497. // IDA: void __usercall NetPlayersChanged(int pNew_count@<EAX>, tNet_game_player_info *pNew_players@<EDX>)
  498. void NetPlayersChanged(int pNew_count, tNet_game_player_info* pNew_players) {
  499.     int i;
  500.     int j;
  501.     int k;
  502.     int switched_res;
  503.     int new_player;
  504.     int player_still_there;
  505.     tPlayer_ID old_fox_it;
  506.     LOG_TRACE("(%d, %p)", pNew_count, pNew_players);
  507.  
  508.     if (gCurrent_net_game->type == eNet_game_type_tag || gCurrent_net_game->type == eNet_game_type_foxy) {
  509. #ifdef DETHRACE_FIX_BUGS
  510.         old_fox_it = -1;
  511.         if (gIt_or_fox >= 0) {
  512. #endif
  513.             old_fox_it = gNet_players[gIt_or_fox].ID;
  514. #ifdef DETHRACE_FIX_BUGS
  515.         }
  516. #endif
  517.     }
  518.     for (i = 0; i < pNew_count; i++) {
  519.         new_player = 1;
  520.         for (j = 0; j < gNumber_of_net_players; j++) {
  521.             if (pNew_players[i].ID == gNet_players[j].ID) {
  522.                 new_player = 0;
  523.                 pNew_players[i].last_waste_message = gNet_players[j].last_waste_message;
  524.                 pNew_players[i].player_status = gNet_players[j].player_status;
  525.                 pNew_players[i].car_index = gNet_players[j].car_index;
  526.                 pNew_players[i].score = gNet_players[j].score;
  527.                 pNew_players[i].credits = gNet_players[j].credits;
  528.                 pNew_players[i].wasted = gNet_players[j].wasted;
  529.                 pNew_players[i].wasteage_attributed = gNet_players[j].wasteage_attributed;
  530.                 pNew_players[i].next_car_index = gNet_players[j].next_car_index;
  531.                 pNew_players[i].car = gNet_players[j].car;
  532.                 break;
  533.             }
  534.         }
  535.         if (new_player) {
  536.             if (pNew_players[i].ID == gLocal_net_ID) {
  537.                 switched_res = SwitchToRealResolution();
  538.                 LoadCar(gOpponents[pNew_players[i].car_index].car_file_name,
  539.                     eDriver_local_human,
  540.                     &gProgram_state.current_car,
  541.                     pNew_players[i].car_index,
  542.                     pNew_players[i].player_name,
  543.                     &gNet_cars_storage_space);
  544.                 if (switched_res) {
  545.                     SwitchToLoresMode();
  546.                 }
  547.                 pNew_players[i].car = &gProgram_state.current_car;
  548.                 if (pNew_players[i].player_status < ePlayer_status_racing) {
  549.                     pNew_players[i].car->disabled = 1;
  550.                 }
  551.                 SetCarStorageTexturingLevel(&gNet_cars_storage_space, GetCarTexturingLevel(), eCTL_full);
  552.             } else {
  553.                 LoadCarN(i, &pNew_players[i]);
  554.             }
  555.             gCurrent_race.opponent_list[i].index = -1;
  556.             gCurrent_race.opponent_list[i].ranking = IRandomBetween(0, 99);
  557.             gCurrent_race.opponent_list[i].car_spec = pNew_players[i].car;
  558.             gCurrent_race.opponent_list[i].net_player_index = i;
  559.             pNew_players[i].opponent_list_index = i;
  560.             gCurrent_race.number_of_racers = pNew_count;
  561.         }
  562.     }
  563.     for (i = 0; i < gNumber_of_net_players; i++) {
  564.         player_still_there = 0;
  565.         for (j = 0; j < pNew_count; j++) {
  566.             if (pNew_players[j].ID == gNet_players[i].ID) {
  567.                 player_still_there = 1;
  568.                 break;
  569.             }
  570.         }
  571.         if (!player_still_there) {
  572.             for (j = 0; j < gNumber_of_net_players; j++) {
  573.                 if (gCurrent_race.opponent_list[j].net_player_index == i) {
  574.                     memmove(&gCurrent_race.opponent_list[j], &gCurrent_race.opponent_list[j + 1], (gNumber_of_net_players - j - 1) * sizeof(tOpp_spec));
  575.                     for (k = 0; k < pNew_count; k++) {
  576.                         if (j < pNew_players[k].opponent_list_index) {
  577.                             pNew_players[k].opponent_list_index--;
  578.                         }
  579.                     }
  580.                     break;
  581.                 }
  582.             }
  583.             PlayerHasLeft(i);
  584.             DisposeCarN(i);
  585.             if (gInitialised_grid) {
  586.                 for (i = 0; i < pNew_count; i++) {
  587.                     gCurrent_race.opponent_list[pNew_players[i].opponent_list_index].net_player_index = i;
  588.                 }
  589.             }
  590.         }
  591.     }
  592.     gNumber_of_net_players = pNew_count;
  593.     memcpy(gNet_players, pNew_players, pNew_count * sizeof(tNet_game_player_info));
  594.     for (i = 0; i < gNumber_of_net_players; i++) {
  595.         gNet_players[i].last_heard_from_him = PDGetTotalTime();
  596.     }
  597.     ForceRebuildActiveCarList();
  598.     if (gCurrent_net_game->type == eNet_game_type_tag || gCurrent_net_game->type == eNet_game_type_foxy) {
  599.         gIt_or_fox = -1;
  600.         for (i = 0; i < gNumber_of_net_players; i++) {
  601.             if (gNet_players[i].ID == old_fox_it) {
  602.                 gIt_or_fox = i;
  603.                 break;
  604.             }
  605.         }
  606.     }
  607. }
  608.  
  609. // IDA: tNet_game_details* __usercall NetHostGame@<EAX>(tNet_game_type pGame_type@<EAX>, tNet_game_options *pOptions@<EDX>, int pStart_rank@<EBX>, char *pHost_name@<ECX>, int pCar_index)
  610. tNet_game_details* NetHostGame(tNet_game_type pGame_type, tNet_game_options* pOptions, int pStart_rank, char* pHost_name, int pCar_index) {
  611.     tNet_game_details* game;
  612.     void* host_address;
  613.     tNet_game_player_info me;
  614.     LOG_TRACE("(%d, %p, %d, \"%s\", %d)", pGame_type, pOptions, pStart_rank, pHost_name, pCar_index);
  615.  
  616.     game = NetAllocatePIDGameDetails();
  617.     if (pHost_name[0] == '\0') {
  618.         sprintf(pHost_name, "%s", "HOST");
  619.     }
  620.     DisableNetService();
  621.     if (PDNetHostGame(game, pHost_name, &host_address) == 0) {
  622.         NetDisposeGameDetails(game);
  623.         return NULL;
  624.     }
  625.     gCurrent_net_game = game;
  626.     gNeed_to_send_start_race = 0;
  627.     strcpy(game->host_name, pHost_name);
  628.     game->host_ID = NetExtractPlayerID(game);
  629.     game->num_players = 1;
  630.     memcpy(&game->options, pOptions, sizeof(tNet_game_options));
  631.     game->status.stage = eNet_game_starting;
  632.     game->type = pGame_type;
  633.     game->start_race = pStart_rank;
  634.     game->no_races_yet = 1;
  635.     gReceiving_new_players = 0;
  636.     gHost_died = 0;
  637.     gNumber_of_net_players = 0;
  638.     gThis_net_player_index = 0;
  639.     gLocal_net_ID = game->host_ID;
  640.     FillInThisPlayer(game, &me, pCar_index, 1);
  641.     gNet_players[0].race_stuff_initialised = 1;
  642.     NetSetPlayerSystemInfo(&me, host_address);
  643.     NetPlayersChanged(1, &me);
  644.     InitialisePlayerStati();
  645.     gNet_mode = eNet_mode_host;
  646.     gDont_allow_joiners = 0;
  647.     return game;
  648. }
  649.  
  650. // IDA: int __usercall NetInitClient@<EAX>(tNet_game_details *pDetails@<EAX>)
  651. int NetInitClient(tNet_game_details* pDetails) {
  652.     LOG_TRACE("(%p)", pDetails);
  653.  
  654.     return PDNetInitClient(pDetails);
  655. }
  656.  
  657. // IDA: int __usercall NetJoinGameLowLevel@<EAX>(tNet_game_details *pDetails@<EAX>, char *pPlayer_name@<EDX>)
  658. int NetJoinGameLowLevel(tNet_game_details* pDetails, char* pPlayer_name) {
  659.     LOG_TRACE("(%p, \"%s\")", pDetails, pPlayer_name);
  660.  
  661.     return PDNetJoinGame(pDetails, pPlayer_name);
  662. }
  663.  
  664. DR_STATIC_ASSERT(offsetof(tNet_message_join, player_info) == 4);
  665. DR_STATIC_ASSERT(offsetof(tNet_game_player_info, this_players_time_stamp) == 0x10);
  666. DR_STATIC_ASSERT(offsetof(tNet_game_player_info, wasted) == 0x68);
  667. DR_STATIC_ASSERT(offsetof(tNet_game_player_info, initial_position) == 0x8c);
  668. DR_STATIC_ASSERT(offsetof(tNet_game_player_info, car) == 0xbc);
  669.  
  670. // IDA: int __usercall NetJoinGame@<EAX>(tNet_game_details *pDetails@<EAX>, char *pPlayer_name@<EDX>, int pCar_index@<EBX>)
  671. int NetJoinGame(tNet_game_details* pDetails, char* pPlayer_name, int pCar_index) {
  672.     int result;
  673.     tNet_message* the_message;
  674.     tU32 start_time;
  675.     LOG_TRACE("(%p, \"%s\", %d)", pDetails, pPlayer_name, pCar_index);
  676.  
  677.     result = NetJoinGameLowLevel(pDetails, pPlayer_name);
  678.     if (result != 0) {
  679.         return result;
  680.     }
  681.     DisableNetService();
  682.     gReceiving_new_players = 0;
  683.     gNet_mode = eNet_mode_client;
  684.     gCurrent_net_game = pDetails;
  685.     gLocal_net_ID = NetExtractPlayerID(pDetails);
  686.     gNumber_of_net_players = 0;
  687.     gLast_player_list_received = 0;
  688.     gJoin_request_denied = 0;
  689.     gCar_was_taken = 0;
  690.     gHost_died = 0;
  691.     the_message = NetBuildMessage(NETMSGID_JOIN, 0);
  692.     FillInThisPlayer(pDetails, &the_message->contents.data.join.player_info, pCar_index, 0);
  693.     ReenableNetService();
  694.     NetGuaranteedSendMessageToAddress(pDetails, the_message, pDetails, NULL);
  695.     start_time = PDGetTotalTime();
  696.     while (1) {
  697.         NetService(0);
  698.         if (gNumber_of_net_players != 0) {
  699.             break;
  700.         }
  701.         if (PDGetTotalTime() - start_time >= 30000 || gJoin_request_denied || gHost_died) {
  702.             break;
  703.         }
  704.     }
  705.     DisableNetService();
  706.     InitialisePlayerStati();
  707.     if (gNumber_of_net_players == 0) {
  708.         ReenableNetService();
  709.         if (gJoin_request_denied && gCar_was_taken) {
  710.             result = -4;
  711.         } else {
  712.             gNet_mode = eNet_mode_none;
  713. #if !defined(DETHRACE_FIX_BUGS)
  714.             // Avoid double free
  715.             NetDisposeGameDetails(gCurrent_net_game);
  716. #endif
  717.             gCurrent_net_game = NULL;
  718.             if (gJoin_request_denied) {
  719.                 result = -2;
  720.             } else {
  721.                 result = -1;
  722.             }
  723.         }
  724.     }
  725.     return result;
  726. }
  727.  
  728. // IDA: void __usercall NetObtainSystemUserName(char *pName@<EAX>, int pMax_length@<EDX>)
  729. void NetObtainSystemUserName(char* pName, int pMax_length) {
  730.  
  731.     PDNetObtainSystemUserName(pName, pMax_length);
  732.     pName[9] = '\0';
  733. }
  734.  
  735. // IDA: tU32 __usercall NetExtractGameID@<EAX>(tNet_game_details *pDetails@<EAX>)
  736. tU32 NetExtractGameID(tNet_game_details* pDetails) {
  737.     LOG_TRACE("(%p)", pDetails);
  738.  
  739.     return PDNetExtractGameID(pDetails);
  740. }
  741.  
  742. // IDA: tPlayer_ID __usercall NetExtractPlayerID@<EAX>(tNet_game_details *pDetails@<EAX>)
  743. tPlayer_ID NetExtractPlayerID(tNet_game_details* pDetails) {
  744.     LOG_TRACE("(%p)", pDetails);
  745.  
  746.     return PDNetExtractPlayerID(pDetails);
  747. }
  748.  
  749. // IDA: int __usercall NetSendMessageToAddress@<EAX>(tNet_game_details *pDetails@<EAX>, tNet_message *pMessage@<EDX>, void *pAddress@<EBX>)
  750. int NetSendMessageToAddress(tNet_game_details* pDetails, tNet_message* pMessage, void* pAddress) {
  751.     LOG_TRACE("(%p, %p, %p)", pDetails, pMessage, pAddress);
  752.  
  753.     if (gNet_mode == eNet_mode_none && !gJoin_list_mode) {
  754.         return -3;
  755.     }
  756.     pMessage->sender = gLocal_net_ID;
  757.     pMessage->senders_time_stamp = PDGetTotalTime();
  758.     GetCheckSum(pMessage);
  759.     return PDNetSendMessageToAddress(pDetails, pMessage, pAddress);
  760. }
  761.  
  762. // IDA: int __usercall NetSendMessageToPlayer@<EAX>(tNet_game_details *pDetails@<EAX>, tNet_message *pMessage@<EDX>, tPlayer_ID pPlayer@<EBX>)
  763. int NetSendMessageToPlayer(tNet_game_details* pDetails, tNet_message* pMessage, tPlayer_ID pPlayer) {
  764.     int i;
  765.     LOG_TRACE("(%p, %p, %d)", pDetails, pMessage, pPlayer);
  766.  
  767.     if (gNet_mode == eNet_mode_none) {
  768.         return -3;
  769.     }
  770.     pMessage->sender = gLocal_net_ID;
  771.     pMessage->senders_time_stamp = PDGetTotalTime();
  772.     for (i = 0; i < gNumber_of_net_players; i++) {
  773.         if (gNet_players[i].ID == pPlayer) {
  774.             GetCheckSum(pMessage);
  775.             return PDNetSendMessageToAddress(pDetails, pMessage, &gNet_players[i]);
  776.         }
  777.     }
  778.     return -3;
  779. }
  780.  
  781. // IDA: int __usercall NetSendMessageToHost@<EAX>(tNet_game_details *pDetails@<EAX>, tNet_message *pMessage@<EDX>)
  782. int NetSendMessageToHost(tNet_game_details* pDetails, tNet_message* pMessage) {
  783.     LOG_TRACE("(%p, %p)", pDetails, pMessage);
  784.  
  785.     if (gNet_mode == eNet_mode_none) {
  786.         return -3;
  787.     }
  788.     pMessage->sender = gLocal_net_ID;
  789.     pMessage->senders_time_stamp = PDGetTotalTime();
  790.     DoCheckSum(pMessage);
  791.     return PDNetSendMessageToAddress(pDetails, pMessage, &pDetails->pd_net_info);
  792. }
  793.  
  794. // IDA: int __usercall NetReplyToMessage@<EAX>(tNet_game_details *pDetails@<EAX>, tNet_message *pIncoming_message@<EDX>, tNet_message *pReply_message@<EBX>)
  795. int NetReplyToMessage(tNet_game_details* pDetails, tNet_message* pIncoming_message, tNet_message* pReply_message) {
  796.     LOG_TRACE("(%p, %p, %p)", pDetails, pIncoming_message, pReply_message);
  797.  
  798.     return NetSendMessageToPlayer(pDetails, pReply_message, pIncoming_message->sender);
  799. }
  800.  
  801. // IDA: int __usercall NetSendMessageToAllPlayers@<EAX>(tNet_game_details *pDetails@<EAX>, tNet_message *pMessage@<EDX>)
  802. int NetSendMessageToAllPlayers(tNet_game_details* pDetails, tNet_message* pMessage) {
  803.     LOG_TRACE("(%p, %p)", pDetails, pMessage);
  804.  
  805.     pMessage->sender = gLocal_net_ID;
  806.     pMessage->senders_time_stamp = PDGetTotalTime();
  807.     GetCheckSum(pMessage);
  808.     return PDNetSendMessageToAllPlayers(pDetails, pMessage);
  809. }
  810.  
  811. // IDA: tU32 __usercall NetGetContentsSize@<EAX>(tNet_message_type pType@<EAX>, tS32 pSize_decider@<EDX>)
  812. tU32 NetGetContentsSize(tNet_message_type pType, tS32 pSize_decider) {
  813.     //tU32 the_size; // Pierre-Marie Baty -- unused variable
  814.     LOG_TRACE("(%d, %d)", pType, pSize_decider);
  815.  
  816.     switch (pType) {
  817.     case NETMSGID_SENDMEDETAILS:
  818.         return sizeof(tNet_message_send_me_details);
  819.     case NETMSGID_DETAILS:
  820.         return sizeof(tNet_message_my_details);
  821.     case NETMSGID_JOIN:
  822.         return sizeof(tNet_message_join);
  823.     case NETMSGID_NEWPLAYERLIST:
  824.         return sizeof(tNet_message_new_player_list);
  825.     case NETMSGID_GUARANTEEREPLY:
  826.         return sizeof(tNet_message_guarantee_reply);
  827.     case NETMSGID_CARDETAILSREQ:
  828.         return sizeof(tNet_message_car_details_req);
  829.     case NETMSGID_CARDETAILS:
  830.         return sizeof(tNet_message_car_details);
  831.     case NETMSGID_LEAVE:
  832.         return sizeof(tNet_message_leave);
  833.     case NETMSGID_HOSTICIDE:
  834.         return sizeof(tNet_message_host_pissing_off);
  835.     case NETMSGID_RACEOVER:
  836.         return sizeof(tNet_message_race_over);
  837.     case NETMSGID_STATUSREPORT:
  838.         return sizeof(tNet_message_status_report);
  839.     case NETMSGID_STARTRACE:
  840.         return sizeof(tNet_message_start_race);
  841.     case NETMSGID_HEADUP:
  842.         return sizeof(tNet_message_headup);
  843.     case NETMSGID_HOSTQUERY:
  844.         return sizeof(tNet_message_host_query);
  845.     case NETMSGID_HOSTREPLY:
  846.         return sizeof(tNet_message_host_reply);
  847.     case NETMSGID_MECHANICS:
  848.         if (pSize_decider == 0) {
  849.             return offsetof(tNet_message_mechanics_info, wheel_dam_offset);
  850.         } else {
  851.             return sizeof(tNet_message_mechanics_info);
  852.         }
  853.     case NETMSGID_NONCAR_INFO:
  854.         return sizeof(tNet_message_non_car_info);
  855.     case NETMSGID_TIMESYNC:
  856.         return sizeof(tNet_message_time_sync);
  857.     case NETMSGID_CONFIRM:
  858.         return sizeof(tNet_message_players_confirm);
  859.     case NETMSGID_DISABLECAR:
  860.         return sizeof(tNet_message_disable_car);
  861.     case NETMSGID_ENABLECAR:
  862.         return sizeof(tNet_message_enable_car);
  863.     case NETMSGID_POWERUP:
  864.         return sizeof(tNet_message_powerup);
  865.     case NETMSGID_RECOVER:
  866.         return sizeof(tNet_message_recover);
  867.     case NETMSGID_SCORES:
  868.         return sizeof(tNet_message_scores);
  869.     case NETMSGID_WASTED:
  870.         return sizeof(tNet_message_wasted);
  871.     case NETMSGID_PEDESTRIAN:
  872.         switch (pSize_decider) {
  873.         case 0:
  874.             return offsetof(tNet_message_pedestrian, to_pos);
  875.         case 1:
  876.             return offsetof(tNet_message_pedestrian, offset);
  877.         case 2:
  878.             return sizeof(tNet_message_pedestrian);
  879.         default:
  880.             TELL_ME_IF_WE_PASS_THIS_WAY();
  881.         }
  882.     case NETMSGID_GAMEPLAY:
  883.         return sizeof(tNet_message_gameplay);
  884.     case NETMSGID_NONCARPOSITION:
  885.         return sizeof(tNet_message_non_car_position);
  886.     case NETMSGID_COPINFO:
  887.         return sizeof(tNet_message_cop_info);
  888.     case NETMSGID_GAMESCORES:
  889.         return sizeof(tNet_message_game_scores);
  890.     case NETMSGID_OILSPILL:
  891.         return sizeof(tNet_message_oil_spill);
  892.     case NETMSGID_CRUSHPOINT:
  893.         return sizeof(tNet_message_crush_point);
  894.     default:
  895.         TELL_ME_IF_WE_PASS_THIS_WAY();
  896.         return 4;
  897.     }
  898. }
  899.  
  900. // IDA: tU32 __usercall NetGetMessageSize@<EAX>(tNet_message_type pType@<EAX>, tS32 pSize_decider@<EDX>)
  901. tU32 NetGetMessageSize(tNet_message_type pType, tS32 pSize_decider) {
  902.     LOG_TRACE("(%d, %d)", pType, pSize_decider);
  903.  
  904.     return NetGetContentsSize(pType, pSize_decider) + sizeof(tNet_message) - sizeof(tNet_contents);
  905. }
  906.  
  907. // IDA: tS32 __usercall NetCalcSizeDecider@<EAX>(tNet_contents *pContents@<EAX>)
  908. tS32 NetCalcSizeDecider(tNet_contents* pContents) {
  909.     //tS32 the_decider; // Pierre-Marie Baty -- unused variable
  910.     LOG_TRACE("(%p)", pContents);
  911.  
  912.     return 0;
  913. }
  914.  
  915. // IDA: tNet_message* __usercall NetBuildMessage@<EAX>(tNet_message_type pType@<EAX>, tS32 pSize_decider@<EDX>)
  916. tNet_message* NetBuildMessage(tNet_message_type pType, tS32 pSize_decider) {
  917.     tNet_message* the_message;
  918.     tU32 the_size;
  919.     LOG_TRACE("(%d, %d)", pType, pSize_decider);
  920.  
  921.     the_size = NetGetMessageSize(pType, pSize_decider);
  922.     the_message = NetAllocateMessage(the_size);
  923.     if (the_message != NULL) {
  924.         the_message->num_contents = 1;
  925.         the_message->overall_size = the_size;
  926.         the_message->contents.header.type = pType;
  927.     }
  928.     return the_message;
  929. }
  930.  
  931. // IDA: tNet_contents* __usercall NetGetToHostContents@<EAX>(tNet_message_type pType@<EAX>, tS32 pSize_decider@<EDX>)
  932. tNet_contents* NetGetToHostContents(tNet_message_type pType, tS32 pSize_decider) {
  933.     tU32 the_size;
  934.     tNet_contents* contents;
  935.     LOG_TRACE("(%d, %d)", pType, pSize_decider);
  936.  
  937.     the_size = NetGetContentsSize(pType, pSize_decider);
  938.     if (gTo_host_stack && the_size + gTo_host_stack->overall_size > MAX_MESAGE_STACK_SIZE) {
  939.         NetSendMessageToHost(gCurrent_net_game, gTo_host_stack);
  940.         gTo_host_stack = 0;
  941.     }
  942.     if (!gTo_host_stack) {
  943.         gTo_host_stack = NetAllocateMessage(MAX_MESAGE_STACK_SIZE);
  944.         gTo_host_stack->overall_size = offsetof(tNet_message, contents);
  945.         gTo_host_stack->num_contents = 0;
  946.     }
  947.     contents = (tNet_contents*)((char*)gTo_host_stack + gTo_host_stack->overall_size);
  948.     gTo_host_stack->overall_size += the_size;
  949.     contents->header.type = pType;
  950.     contents->header.contents_size = the_size;
  951.     gTo_host_stack->num_contents++;
  952.     return contents;
  953. }
  954.  
  955. // IDA: tNet_contents* __usercall NetGetBroadcastContents@<EAX>(tNet_message_type pType@<EAX>, tS32 pSize_decider@<EDX>)
  956. tNet_contents* NetGetBroadcastContents(tNet_message_type pType, tS32 pSize_decider) {
  957.     tU32 the_size;
  958.     tNet_contents* contents;
  959.     LOG_TRACE("(%d, %d)", pType, pSize_decider);
  960.  
  961.     the_size = NetGetContentsSize(pType, pSize_decider);
  962.     if (gBroadcast_stack && the_size + gBroadcast_stack->overall_size > MAX_MESAGE_STACK_SIZE) {
  963.         NetSendMessageToAllPlayers(gCurrent_net_game, gBroadcast_stack);
  964.         gBroadcast_stack = NULL;
  965.     }
  966.     if (gBroadcast_stack == NULL) {
  967.         gBroadcast_stack = NetAllocateMessage(MAX_MESAGE_STACK_SIZE);
  968.         gBroadcast_stack->overall_size = offsetof(tNet_message, contents);
  969.         gBroadcast_stack->num_contents = 0;
  970.     }
  971.     contents = (tNet_contents*)((char*)gBroadcast_stack + gBroadcast_stack->overall_size);
  972.     gBroadcast_stack->overall_size += the_size;
  973.     contents->header.type = pType;
  974.     contents->header.contents_size = the_size;
  975.     gBroadcast_stack->num_contents++;
  976.     return contents;
  977. }
  978.  
  979. // IDA: void __cdecl NetSendMessageStacks()
  980. void NetSendMessageStacks(void) {
  981.     LOG_TRACE("()");
  982.  
  983.     gLast_flush_message = PDGetTotalTime();
  984.     if (gBroadcast_stack != NULL) {
  985.         NetSendMessageToAllPlayers(gCurrent_net_game, gBroadcast_stack);
  986.         gBroadcast_stack = NULL;
  987.     }
  988.     if (gTo_host_stack != NULL) {
  989.         NetSendMessageToHost(gCurrent_net_game, gTo_host_stack);
  990.         gTo_host_stack = NULL;
  991.     }
  992. }
  993.  
  994. // IDA: tNet_message* __usercall NetAllocateMessage@<EAX>(int pSize@<EAX>)
  995. tNet_message* NetAllocateMessage(int pSize) {
  996.     void* pointer;
  997.     void* last_message;
  998.     //char* test; // Pierre-Marie Baty -- unused variable
  999.     static int rr_min = 0; // Pierre-Marie Baty -- uninitialized static variable
  1000.     static int rr_mid = 0; // Pierre-Marie Baty -- uninitialized static variable
  1001.     static int rr_max = 0; // Pierre-Marie Baty -- uninitialized static variable
  1002.     tNet_message* message;
  1003.     int i;
  1004.     LOG_TRACE("(%d)", pSize);
  1005.  
  1006.     pointer = NULL;
  1007.     if (pSize <= sizeof(tMin_message) - sizeof(void*)) {
  1008.         for (i = 0; i < MIN_MESSAGES_CAPACITY; i++) {
  1009.             if (((tNet_message*)&gMin_messages[rr_min])->contents.header.type == NETMSGID_NONE) {
  1010.                 pointer = &gMin_messages[rr_min];
  1011.                 break;
  1012.             }
  1013.             rr_min++;
  1014.             if (rr_min >= MIN_MESSAGES_CAPACITY) {
  1015.                 rr_min = 0;
  1016.             }
  1017.         }
  1018.     }
  1019.     if (pointer == NULL && pSize <= sizeof(tMid_message) - sizeof(void*)) {
  1020.         for (i = 0; i < MID_MESSAGES_CAPACITY; i++) {
  1021.             if (((tNet_message*)&gMid_messages[rr_mid])->contents.header.type == NETMSGID_NONE) {
  1022.                 pointer = &gMid_messages[rr_mid];
  1023.                 break;
  1024.             }
  1025.             rr_mid++;
  1026.             if (rr_mid >= MID_MESSAGES_CAPACITY) {
  1027.                 rr_mid = 0;
  1028.             }
  1029.         }
  1030.     }
  1031.     if (pointer == NULL && pSize <= sizeof(tMax_message) - sizeof(void*)) {
  1032.         for (i = 0; i < MAX_MESSAGES_CAPACITY; i++) {
  1033.             if (((tNet_message*)&gMax_messages[rr_max])->contents.header.type == NETMSGID_NONE) {
  1034.                 pointer = &gMax_messages[rr_max];
  1035.                 break;
  1036.             }
  1037.             rr_max++;
  1038.             if (rr_max >= MAX_MESSAGES_CAPACITY) {
  1039.                 rr_max = 0;
  1040.             }
  1041.         }
  1042.     }
  1043.     if (pointer == NULL) {
  1044.         pointer = BrMemAllocate(gMessage_header_size + pSize + sizeof(void*), kMem_dynamic_message);
  1045.         if (pointer != NULL) {
  1046.             *(void**)pointer = NULL;
  1047.             if (gMessage_to_free != NULL) {
  1048.                 for (last_message = gMessage_to_free; *(void**)last_message != NULL; last_message = *(void**)last_message) {
  1049.                 }
  1050.                 *(void**)last_message = pointer;
  1051.             } else {
  1052.                 gMessage_to_free = pointer;
  1053.             }
  1054.             pointer = (char*)pointer + sizeof(void*);
  1055.         }
  1056.     }
  1057.     if (pointer == NULL) {
  1058.         LOG_PANIC("null pointer!");
  1059.         message = NULL;
  1060.     } else {
  1061.         message = (tNet_message*)((tU8*)pointer + gMessage_header_size);
  1062.         message->guarantee_number = 0;
  1063.         message->version = 1;
  1064.         message->magic_number = 0x763a5058;
  1065.     }
  1066.     return message;
  1067. }
  1068.  
  1069. // IDA: void __cdecl NetFreeExcessMemory()
  1070. void NetFreeExcessMemory(void) {
  1071.     void* temp;
  1072.     LOG_TRACE("()");
  1073.  
  1074.     while (gMessage_to_free != NULL && ((tNet_message*)((char*)gMessage_to_free + sizeof(void*)))->contents.header.type == NETMSGID_NONE) {
  1075.         temp = *(void**)gMessage_to_free;
  1076.         BrMemFree(gMessage_to_free);
  1077.         gMessage_to_free = temp;
  1078.     }
  1079. }
  1080.  
  1081. // IDA: int __usercall NetDisposeMessage@<EAX>(tNet_game_details *pDetails@<EAX>, tNet_message *pMessage@<EDX>)
  1082. int NetDisposeMessage(tNet_game_details* pDetails, tNet_message* pMessage) {
  1083.     LOG_TRACE("(%p, %p)", pDetails, pMessage);
  1084.  
  1085.     if (pMessage->guarantee_number != 0) {
  1086.         return -1;
  1087.     }
  1088.     pMessage->contents.header.type = NETMSGID_NONE;
  1089.     return 0;
  1090. }
  1091.  
  1092. // IDA: tNet_message* __usercall NetGetNextMessage@<EAX>(tNet_game_details *pDetails@<EAX>, void **pSender_address@<EDX>)
  1093. tNet_message* NetGetNextMessage(tNet_game_details* pDetails, void** pSender_address) {
  1094.     LOG_TRACE("(%p, %p)", pDetails, pSender_address);
  1095.  
  1096.     return PDNetGetNextMessage(pDetails, pSender_address);
  1097. }
  1098.  
  1099. // IDA: void __usercall ReceivedSendMeDetails(tNet_contents *pContents@<EAX>, void *pSender_address@<EDX>)
  1100. void ReceivedSendMeDetails(tNet_contents* pContents, void* pSender_address) {
  1101.     tNet_message* message;
  1102.     LOG_TRACE("(%p, %p)", pContents, pSender_address);
  1103.  
  1104.     if (gDont_allow_joiners) {
  1105.         return;
  1106.     }
  1107.     message = NetBuildMessage(NETMSGID_DETAILS, 0);
  1108.     memcpy(&message->contents.data.details.details.host_name, gCurrent_net_game->host_name, sizeof(*gCurrent_net_game) - offsetof(tNet_game_details, host_name));
  1109.     NetSendMessageToAddress(gCurrent_net_game, message, pSender_address);
  1110. }
  1111.  
  1112. // IDA: void __usercall ReceivedDetails(tNet_contents *pContents@<EAX>)
  1113. void ReceivedDetails(tNet_contents* pContents) {
  1114.     LOG_TRACE("(%p)", pContents);
  1115.  
  1116.     if (gCurrent_join_poll_game == NULL) {
  1117.         return;
  1118.     }
  1119.     gBastard_has_answered = 1;
  1120.     memcpy(gCurrent_join_poll_game->host_name, &pContents->data.details.details.host_name, sizeof(*gCurrent_join_poll_game) - offsetof(tNet_game_details, host_name));
  1121. }
  1122.  
  1123. // IDA: void __cdecl SendOutPlayerList()
  1124. void SendOutPlayerList(void) {
  1125.     tNet_message* message;
  1126.     int i;
  1127.     LOG_TRACE("()");
  1128.  
  1129.     gCurrent_net_game->num_players = gNumber_of_net_players;
  1130.     for (i = 0; i < gNumber_of_net_players; i++) {
  1131.         message = NetBuildMessage(NETMSGID_NEWPLAYERLIST, 1);
  1132.         message->contents.data.player_list.number_of_players = gNumber_of_net_players;
  1133.         message->contents.data.player_list.this_index = i;
  1134.         message->contents.data.player_list.batch_number = gPlayer_list_batch_number;
  1135.         memcpy(&message->contents.data.player_list.player, &gNet_players[i], sizeof(gNet_players[i]));
  1136.         NetGuaranteedSendMessageToAllPlayers(gCurrent_net_game, message, 0);
  1137.     }
  1138.     gPlayer_list_batch_number++;
  1139.     if (gInitialised_grid) {
  1140.         SetUpNetCarPositions();
  1141.         if (gStart_race_sent) {
  1142.             gNeed_to_send_start_race = 1;
  1143.             for (i = 1; i < gNumber_of_net_players; i++) {
  1144.                 gNet_players[i].awaiting_confirmation = 1;
  1145.             }
  1146.         }
  1147.     }
  1148. }
  1149.  
  1150. // IDA: void __usercall ReceivedJoin(tNet_contents *pContents@<EAX>, void *pSender_address@<EDX>)
  1151. void ReceivedJoin(tNet_contents* pContents, void* pSender_address) {
  1152.     int i;
  1153.     int new_player_count;
  1154.     int slot_index;
  1155.     tNet_message* message;
  1156.     tNet_game_player_info* new_players;
  1157.     LOG_TRACE("(%p, %p)", pContents, pSender_address);
  1158.  
  1159.     new_player_count = gNumber_of_net_players;
  1160.     new_players = BrMemAllocate((new_player_count + 1) * sizeof(tNet_game_player_info), kMem_player_list_join);
  1161.     memcpy(new_players, gNet_players, gNumber_of_net_players * sizeof(tNet_game_player_info));
  1162.  
  1163.     if ((!gCurrent_net_game->options.open_game && gProgram_state.racing) || gCurrent_net_game->num_players > 5 || gDont_allow_joiners) {
  1164.         message = NetBuildMessage(NETMSGID_NEWPLAYERLIST, 0);
  1165.         // Send player count = 0 when race has already begun or is full
  1166.         message->contents.data.player_list.number_of_players = 0;
  1167.         NetSendMessageToAddress(gCurrent_net_game, message, pSender_address);
  1168.     } else {
  1169.         for (i = 0; i < new_player_count; i++) {
  1170.             if (new_players[i].ID == pContents->data.join.player_info.ID) {
  1171.                 return;
  1172.             }
  1173.         }
  1174.         slot_index = new_player_count;
  1175.         new_player_count++;
  1176.         if (pContents->data.join.player_info.car_index < 0) {
  1177.             pContents->data.join.player_info.car_index = PickARandomCar();
  1178.         } else {
  1179.             for (i = 0; i < gNumber_of_net_players; i++) {
  1180.                 if (gNet_players[i].car_index == pContents->data.join.player_info.car_index) {
  1181.                     message = NetBuildMessage(NETMSGID_NEWPLAYERLIST, 0);
  1182.                     // Send player count = -1 when selected car is unavailable
  1183.                     message->contents.data.player_list.number_of_players = -1;
  1184.                     NetSendMessageToAddress(gCurrent_net_game, message, pSender_address);
  1185.                     return;
  1186.                 }
  1187.             }
  1188.         }
  1189.         if (pContents->data.join.player_info.car_index >= 0) {
  1190.             gCar_details[pContents->data.join.player_info.car_index].ownership = eCar_owner_someone;
  1191.         }
  1192.         memcpy(&new_players[slot_index], &pContents->data.join.player_info, sizeof(tNet_game_player_info));
  1193.         new_players[slot_index].player_status = ePlayer_status_loading;
  1194.         new_players[slot_index].last_heard_from_him = PDGetTotalTime();
  1195.         new_players[slot_index].grid_position_set = 0;
  1196.         if (new_players[slot_index].player_name[0] == '\0') {
  1197.             sprintf(new_players[slot_index].player_name, "%s %d", "PLAYER", slot_index);
  1198.         }
  1199.         NetSetPlayerSystemInfo(&new_players[slot_index], pSender_address);
  1200.         NetPlayersChanged(new_player_count, new_players);
  1201.         BrMemFree(new_players);
  1202.         SendOutPlayerList();
  1203.     }
  1204. }
  1205.  
  1206. // IDA: void __usercall KickPlayerOut(tPlayer_ID pID@<EAX>)
  1207. void KickPlayerOut(tPlayer_ID pID) {
  1208.     int i;
  1209.     int j;
  1210.     int new_player_count;
  1211.     tNet_game_player_info* new_players;
  1212.     LOG_TRACE("(%d)", pID);
  1213.  
  1214.     new_player_count = gNumber_of_net_players;
  1215.     new_players = (tNet_game_player_info*)BrMemAllocate(sizeof(tNet_game_player_info) * gNumber_of_net_players, kMem_player_list_leave);
  1216.     memcpy(new_players, gNet_players, sizeof(tNet_game_player_info) * new_player_count);
  1217.     for (i = 0; i < new_player_count; i++) {
  1218.         if (new_players[i].ID == pID) {
  1219.             if (new_players[i].car_index >= 0) {
  1220.                 gCar_details[new_players[i].car_index].ownership = eCar_owner_none;
  1221.             }
  1222.             if (new_players[i].next_car_index >= 0 && gCurrent_net_game->options.random_car_choice && (gCurrent_net_game->options.car_choice == eNet_car_all || gCurrent_net_game->options.car_choice == eNet_car_both)) {
  1223.                 gCar_details[new_players[i].next_car_index].ownership = eCar_owner_none;
  1224.             }
  1225. #ifdef DETHRACE_FIX_BUGS
  1226.             for (j = i; j < new_player_count - 1; j++) {
  1227. #else
  1228.             for (j = i; j < new_player_count; j++) {
  1229. #endif
  1230.                 memcpy(&new_players[j], &new_players[j + 1], sizeof(tNet_game_player_info));
  1231.             }
  1232.             new_player_count--;
  1233.             break;
  1234.         }
  1235.     }
  1236.     NetPlayersChanged(new_player_count, new_players);
  1237.     BrMemFree(new_players);
  1238.     SendOutPlayerList();
  1239. }
  1240.  
  1241. // IDA: void __usercall ReceivedLeave(tNet_contents *pContents@<EAX>, tNet_message *pMessage@<EDX>)
  1242. void ReceivedLeave(tNet_contents* pContents, tNet_message* pMessage) {
  1243.     LOG_TRACE("(%p, %p)", pContents, pMessage);
  1244.  
  1245.     KickPlayerOut(pMessage->sender);
  1246. }
  1247.  
  1248. // IDA: void __usercall NetFullScreenMessage(int pStr_index@<EAX>, int pLeave_it_up_there@<EDX>)
  1249. void NetFullScreenMessage(int pStr_index, int pLeave_it_up_there) {
  1250.     tU32 start_time;
  1251.     char* s;
  1252.     // Jeff: added underscore suffix to avoid collisions with samed-named globals
  1253.     int gPixel_buffer_size_;
  1254.     char* gPixels_copy_;
  1255.     char* gPalette_copy_;
  1256.     int restore_screen;
  1257.  
  1258.     LOG_TRACE("(%d, %d)", pStr_index, pLeave_it_up_there);
  1259.  
  1260.     if (pLeave_it_up_there || (gProgram_state.racing && !gInterface_within_race_mode)) {
  1261.         restore_screen = 0;
  1262.     } else {
  1263.         gPixel_buffer_size_ = gBack_screen->height * gBack_screen->row_bytes;
  1264.         gPixels_copy_ = BrMemAllocate(gPixel_buffer_size_, 0xB0u);
  1265.         gPalette_copy_ = BrMemAllocate(0x400u, 0xB1u);
  1266.         memcpy(gPixels_copy_, gBack_screen->pixels, gPixel_buffer_size_);
  1267.         memcpy(gPalette_copy_, gCurrent_palette_pixels, 0x400u);
  1268.         restore_screen = 1;
  1269.     }
  1270.     FadePaletteDown();
  1271.     LoadFont(FONT_MEDIUMHD);
  1272.     ClearEntireScreen();
  1273.     if (pStr_index <= 0) {
  1274.         s = "FIXED THAT YOU TWISTED BASTARDS";
  1275.     } else {
  1276.         s = GetMiscString(pStr_index);
  1277.     }
  1278.     OoerrIveGotTextInMeBoxMissus(
  1279.         FONT_MEDIUMHD,
  1280.         s,
  1281.         gBack_screen,
  1282.         0,
  1283.         gGraf_specs[gGraf_spec_index].total_height / 2 - gFonts[kFont_MEDIUMHD].height,
  1284.         gGraf_specs[gGraf_spec_index].total_width,
  1285.         gGraf_specs[gGraf_spec_index].total_height,
  1286.         1);
  1287.     PDScreenBufferSwap(0);
  1288.     EnsureRenderPalette();
  1289.     EnsurePaletteUp();
  1290.     if (!pLeave_it_up_there) {
  1291.         start_time = PDGetTotalTime();
  1292.         while (PDGetTotalTime() - start_time < 3000) {
  1293.             ;
  1294.         }
  1295.         FadePaletteDown();
  1296.         if (restore_screen) {
  1297.             memcpy(gBack_screen->pixels, gPixels_copy_, gPixel_buffer_size_);
  1298.             memcpy(gCurrent_palette_pixels, gPalette_copy_, 0x400u);
  1299.             BrMemFree(gPixels_copy_);
  1300.             BrMemFree(gPalette_copy_);
  1301.             PDScreenBufferSwap(0);
  1302.             FadePaletteUp();
  1303.         } else {
  1304.             ClearEntireScreen();
  1305.         }
  1306.     }
  1307. }
  1308.  
  1309. // IDA: void __usercall HostHasBittenTheDust(int pMessage_index@<EAX>)
  1310. void HostHasBittenTheDust(int pMessage_index) {
  1311.     LOG_TRACE("(%d)", pMessage_index);
  1312.  
  1313.     if (!gHost_died) {
  1314.         gHost_died = 1;
  1315.         NetFullScreenMessage(pMessage_index, 0);
  1316.         gProgram_state.prog_status = eProg_idling;
  1317.     }
  1318. }
  1319.  
  1320. // IDA: void __usercall ReceivedHosticide(tNet_contents *pContents@<EAX>)
  1321. void ReceivedHosticide(tNet_contents* pContents) {
  1322.     LOG_TRACE("(%p)", pContents);
  1323.  
  1324.     HostHasBittenTheDust(kMiscString_GAME_TERMINATED_BY_HOST);
  1325. }
  1326.  
  1327. // IDA: void __cdecl ConfirmReceipt()
  1328. void ConfirmReceipt(void) {
  1329.     //tNet_message* the_message; // Pierre-Marie Baty -- unused variable
  1330.     LOG_TRACE("()");
  1331.     NOT_IMPLEMENTED();
  1332. }
  1333.  
  1334. // IDA: void __usercall ReceivedNewPlayerList(tNet_contents *pContents@<EAX>, tNet_message *pM@<EDX>)
  1335. void ReceivedNewPlayerList(tNet_contents* pContents, tNet_message* pM) {
  1336.     int i;
  1337.     LOG_TRACE("(%p, %p)", pContents, pM);
  1338.  
  1339.     if (pContents->data.player_list.number_of_players <= 0) {
  1340.         gJoin_request_denied = 1;
  1341.         if (pContents->data.player_list.number_of_players < 0) {
  1342.             gCar_was_taken = 1;
  1343.         }
  1344.         return;
  1345.     }
  1346.     if (pContents->data.player_list.batch_number >= gReceiving_batch_number) {
  1347.         if (!gReceiving_new_players) {
  1348.             gLast_player_list_received = pM->senders_time_stamp;
  1349.             for (i = 0; i < COUNT_OF(gNew_net_players); i++) {
  1350.                 gNew_net_players[i].car_index = -1;
  1351.             }
  1352.             gReceiving_new_players = 1;
  1353.             gReceiving_batch_number = pContents->data.player_list.batch_number;
  1354.         }
  1355.         if (pContents->data.player_list.batch_number <= gReceiving_batch_number) {
  1356.             memcpy(&gNew_net_players[pContents->data.player_list.this_index], &pContents->data.player_list.player, sizeof(tNet_game_player_info));
  1357.             for (i = 0; i < pContents->data.player_list.number_of_players; i++) {
  1358.                 if (gNew_net_players[i].car_index < 0) {
  1359.                     return;
  1360.                 }
  1361.             }
  1362.             gReceiving_new_players = 0;
  1363.             NetPlayersChanged(pContents->data.player_list.number_of_players, gNew_net_players);
  1364.             gThis_net_player_index = -1;
  1365.             for (i = 0; i < gNumber_of_net_players; i++) {
  1366.                 if (gNet_players[i].ID == gLocal_net_ID) {
  1367.                     gThis_net_player_index = i;
  1368.                     break;
  1369.                 }
  1370.             }
  1371.             if (gThis_net_player_index < 0) {
  1372.                 FatalError(kFatalError_NotInReceivedPlayerList);
  1373.             }
  1374.             gNet_players[0].last_heard_from_him = PDGetTotalTime();
  1375.             gCurrent_race.number_of_racers = gNumber_of_net_players;
  1376.             if (gSynch_race_start) {
  1377.                 for (i = 0; i < gNumber_of_net_players; i++) {
  1378.                     gCurrent_race.opponent_list[gNet_players[i].opponent_list_index].net_player_index = i;
  1379.                 }
  1380.             }
  1381.         } else {
  1382.             gReceiving_new_players = 0;
  1383.             ReceivedNewPlayerList(pContents, pM);
  1384.         }
  1385.     }
  1386. }
  1387.  
  1388. // IDA: void __usercall ReceivedRaceOver(tNet_contents *pContents@<EAX>)
  1389. void ReceivedRaceOver(tNet_contents* pContents) {
  1390.     LOG_TRACE("(%p)", pContents);
  1391.  
  1392.     gRace_finished = 0;
  1393.     if (gProgram_state.racing && (gNet_mode == eNet_mode_client || pContents->data.race_over.reason == eRace_over_network_victory || pContents->data.race_over.reason == eRace_over_network_loss)) {
  1394.         RaceCompleted(pContents->data.race_over.reason);
  1395.     }
  1396. }
  1397.  
  1398. // IDA: void __usercall ReceivedStatusReport(tNet_contents *pContents@<EAX>, tNet_message *pMessage@<EDX>)
  1399. void ReceivedStatusReport(tNet_contents* pContents, tNet_message* pMessage) {
  1400.     int i;
  1401.     LOG_TRACE("(%p, %p)", pContents, pMessage);
  1402.  
  1403.     for (i = 0; i < gNumber_of_net_players; i++) {
  1404.         if (gNet_players[i].ID == pMessage->sender) {
  1405.             gNet_players[i].player_status = pContents->data.report.status;
  1406.             gNet_players[i].last_heard_from_him = PDGetTotalTime();
  1407.             if (gNet_players[i].player_status < ePlayer_status_racing || gNet_players[i].player_status == ePlayer_status_recovering) {
  1408.                 if (gNet_players[i].player_status < ePlayer_status_racing) {
  1409.                     DisableCar(gNet_players[i].car);
  1410.                 }
  1411.             } else {
  1412.                 if (gNet_players[i].car->disabled) {
  1413.                     SendCurrentPowerups();
  1414.                 }
  1415.                 EnableCar(gNet_players[i].car);
  1416.             }
  1417.             return;
  1418.         }
  1419.     }
  1420. }
  1421.  
  1422. // IDA: void __usercall ReceivedStartRace(tNet_contents *pContents@<EAX>)
  1423. void ReceivedStartRace(tNet_contents* pContents) {
  1424.     int i;
  1425.     int index;
  1426.     LOG_TRACE("(%p)", pContents);
  1427.  
  1428.     if (pContents->data.player_list.number_of_players == -1) {
  1429.         if (gProgram_state.racing) {
  1430.             index = pContents->data.start_race.car_list[0].index;
  1431.             BrMatrix34Copy(&gNet_players[index].car->car_master_actor->t.t.mat, &pContents->data.start_race.car_list[0].mat);
  1432.             ReinitialiseCar(gNet_players[index].car);
  1433.             if (gThis_net_player_index == index) {
  1434.                 if (!gInterface_within_race_mode) {
  1435.                     FadePaletteDown();
  1436.                 }
  1437.                 CancelPendingCunningStunt();
  1438.                 gProgram_state.credits_earned = gInitial_net_credits[gCurrent_net_game->options.starting_money_index];
  1439.                 gProgram_state.credits_lost = 0;
  1440.                 InitialiseExternalCamera();
  1441.             }
  1442.             gNet_players[index].last_waste_message = 0;
  1443.             gNet_players[index].wasteage_attributed = 0;
  1444.         }
  1445.     } else if (gSynch_race_start) {
  1446.         for (i = 0; i < pContents->data.start_race.car_count; i++) {
  1447.             gNet_players[pContents->data.start_race.car_list[i].index].next_car_index = pContents->data.start_race.car_list[i].next_car_index;
  1448.         }
  1449.     } else {
  1450.         for (i = 0; i < pContents->data.player_list.number_of_players; i++) {
  1451.             gCurrent_race.number_of_racers = i + 1;
  1452.             gCurrent_race.opponent_list[i].index = -1;
  1453.             gCurrent_race.opponent_list[i].ranking = -1;
  1454.             gCurrent_race.opponent_list[i].car_spec = gNet_players[pContents->data.start_race.car_list[i].index].car;
  1455.             gCurrent_race.opponent_list[i].net_player_index = pContents->data.start_race.car_list[i].index;
  1456.             gNet_players[gCurrent_race.opponent_list[i].net_player_index].last_waste_message = 0;
  1457.             gNet_players[gCurrent_race.opponent_list[i].net_player_index].wasteage_attributed = 0;
  1458.             if (!gProgram_state.racing || gCurrent_race.opponent_list[i].car_spec->driver != eDriver_local_human) {
  1459.                 BrMatrix34Copy(&gCurrent_race.opponent_list[i].car_spec->car_master_actor->t.t.mat, &pContents->data.start_race.car_list[i].mat);
  1460.                 InitialiseCar(gCurrent_race.opponent_list[i].car_spec);
  1461.             }
  1462.             gNet_players[pContents->data.start_race.car_list[i].index].next_car_index = pContents->data.start_race.car_list[i].next_car_index;
  1463.         }
  1464.         gPending_race = pContents->data.player_list.batch_number;
  1465.         gCurrent_race.number_of_racers = pContents->data.player_list.number_of_players;
  1466.         gSynch_race_start = 1;
  1467.         if (!pContents->data.player_list.this_index || gProgram_state.racing) {
  1468.             gWait_for_it = 0;
  1469.         }
  1470.     }
  1471. }
  1472.  
  1473. // IDA: void __usercall ReceivedGuaranteeReply(tNet_contents *pContents@<EAX>)
  1474. void ReceivedGuaranteeReply(tNet_contents* pContents) {
  1475.     int i;
  1476.     LOG_TRACE("(%p)", pContents);
  1477.  
  1478.     for (i = 0; i < gNext_guarantee; i++) {
  1479.         if (gGuarantee_list[i].guarantee_number == pContents->data.reply.guarantee_number) {
  1480.             gGuarantee_list[i].recieved = 1;
  1481.         }
  1482.     }
  1483. }
  1484.  
  1485. // IDA: void __usercall ReceivedHeadup(tNet_contents *pContents@<EAX>)
  1486. void ReceivedHeadup(tNet_contents* pContents) {
  1487.     LOG_TRACE("(%p)", pContents);
  1488.  
  1489.     if (gProgram_state.racing) {
  1490.         NewTextHeadupSlot(4, 0, 3000, -4, pContents->data.headup.text);
  1491.     }
  1492. }
  1493.  
  1494. // IDA: void __usercall ReceivedHostQuery(tNet_contents *pContents@<EAX>, tNet_message *pMessage@<EDX>)
  1495. void ReceivedHostQuery(tNet_contents* pContents, tNet_message* pMessage) {
  1496.     tNet_message* message;
  1497.     LOG_TRACE("(%p, %p)", pContents, pMessage);
  1498.  
  1499.     message = NetBuildMessage(NETMSGID_HOSTREPLY, 0);
  1500.     message->contents.data.heres_where_we_at.race_has_started = gProgram_state.racing;
  1501.     message->contents.data.heres_where_we_at.race_index = gProgram_state.current_race_index;
  1502.     message->contents.data.heres_where_we_at.pending_race = gPending_race;
  1503.     NetGuaranteedSendMessageToPlayer(gCurrent_net_game, message, pMessage->sender, NULL);
  1504.     if (gProgram_state.racing) {
  1505.         SignalToStartRace();
  1506.         UpdateEnvironments();
  1507.     }
  1508. }
  1509.  
  1510. // IDA: void __usercall ReceivedHostReply(tNet_contents *pContents@<EAX>)
  1511. void ReceivedHostReply(tNet_contents* pContents) {
  1512.     //tNet_message* message; // Pierre-Marie Baty -- unused variable
  1513.     LOG_TRACE("(%p)", pContents);
  1514.  
  1515.     if (pContents->data.heres_where_we_at.race_index != gProgram_state.current_race_index) {
  1516.         NetLeaveGame(gCurrent_net_game);
  1517.         NetFullScreenMessage(kMiscString_RACE_CHANGED_DURING_LOADING, 0);
  1518.         gProgram_state.prog_status = eProg_idling;
  1519.     }
  1520.     if (pContents->data.heres_where_we_at.race_has_started) {
  1521.         if (gCurrent_net_game->options.open_game) {
  1522.             gPending_race = pContents->data.heres_where_we_at.pending_race;
  1523.         } else {
  1524.             NetFullScreenMessage(kMiscString_SORRY_YOU_RE_TOO_LATE, 0);
  1525.             gProgram_state.prog_status = eProg_idling;
  1526.         }
  1527.     }
  1528. }
  1529.  
  1530. // IDA: void __usercall SendGuaranteeReply(tNet_message *pMessage@<EAX>, void *pSender_address@<EDX>)
  1531. void SendGuaranteeReply(tNet_message* pMessage, void* pSender_address) {
  1532.     tNet_message* message;
  1533.     LOG_TRACE("(%p, %p)", pMessage, pSender_address);
  1534.  
  1535.     message = NetBuildMessage(NETMSGID_GUARANTEEREPLY, 0);
  1536.     message->contents.data.reply.guarantee_number = pMessage->guarantee_number;
  1537.     pMessage->guarantee_number = 0;
  1538.     NetSendMessageToAddress(gCurrent_net_game, message, pSender_address);
  1539. }
  1540.  
  1541. // IDA: int __usercall PlayerIsInList@<EAX>(tPlayer_ID pID@<EAX>)
  1542. int PlayerIsInList(tPlayer_ID pID) {
  1543.     int i;
  1544.     LOG_TRACE("(%d)", pID);
  1545.  
  1546.     for (i = 0; i < gNumber_of_net_players; i++) {
  1547.         if (gNet_players[i].ID == pID) {
  1548.             gNet_players[i].last_heard_from_him = PDGetTotalTime();
  1549.             return 1;
  1550.         }
  1551.     }
  1552.     return 0;
  1553. }
  1554.  
  1555. // IDA: void __usercall ReceivedTimeSync(tNet_contents *pContents@<EAX>, tNet_message *pMessage@<EDX>, tU32 pReceive_time@<EBX>)
  1556. void ReceivedTimeSync(tNet_contents* pContents, tNet_message* pMessage, tU32 pReceive_time) {
  1557.     LOG_TRACE("(%p, %p, %d)", pContents, pMessage, pReceive_time);
  1558.  
  1559.     if (pMessage->senders_time_stamp - pContents->data.time_sync.race_start_time > pReceive_time + 10) {
  1560.         gRace_start -= pMessage->senders_time_stamp - pContents->data.time_sync.race_start_time - pReceive_time;
  1561.     }
  1562. }
  1563.  
  1564. // IDA: void __usercall ReceivedConfirm(tNet_contents *pContents@<EAX>)
  1565. void ReceivedConfirm(tNet_contents* pContents) {
  1566.     int i;
  1567.     LOG_TRACE("(%p)", pContents);
  1568.  
  1569.     for (i = 0; i < gNumber_of_net_players; i++) {
  1570.         if (gNet_players[i].ID == pContents->data.confirm.player) {
  1571.             gNet_players[i].awaiting_confirmation = 0;
  1572.             return;
  1573.         }
  1574.     }
  1575. }
  1576.  
  1577. // IDA: void __usercall ReceivedDisableCar(tNet_contents *pContents@<EAX>)
  1578. void ReceivedDisableCar(tNet_contents* pContents) {
  1579.     LOG_TRACE("(%p)", pContents);
  1580.  
  1581. }
  1582.  
  1583. // IDA: void __usercall ReceivedEnableCar(tNet_contents *pContents@<EAX>)
  1584. void ReceivedEnableCar(tNet_contents* pContents) {
  1585.     LOG_TRACE("(%p)", pContents);
  1586.  
  1587. }
  1588.  
  1589. // IDA: void __usercall ReceivedScores(tNet_contents *pContents@<EAX>)
  1590. void ReceivedScores(tNet_contents* pContents) {
  1591.     int i;
  1592.     LOG_TRACE("(%p)", pContents);
  1593.  
  1594.     UseGeneralScore(pContents->data.scores.general_score);
  1595.     for (i = 0; i < gNumber_of_net_players; i++) {
  1596.         gNet_players[i].score = pContents->data.scores.scores[i];
  1597.     }
  1598. }
  1599.  
  1600. // IDA: void __usercall ReceivedWasted(tNet_contents *pContents@<EAX>)
  1601. void ReceivedWasted(tNet_contents* pContents) {
  1602.     tNet_game_player_info* victim;
  1603.     tNet_game_player_info* culprit;
  1604.     char s[256];
  1605.     tCar_spec* car;
  1606.     static tS32 last_got_wasted_time;
  1607.     static tS32 last_wasted_em_time;
  1608.     static tS32 last_wasty_message_time;
  1609.     static tNet_game_player_info* last_culprit;
  1610.     static tNet_game_player_info* last_victim;
  1611.     LOG_TRACE("(%p)", pContents);
  1612.  
  1613.     victim = NetPlayerFromID(pContents->data.wasted.victim);
  1614.     if (victim == NULL) {
  1615.         return;
  1616.     }
  1617.     victim->car->knackered = 1;
  1618.     if (pContents->data.wasted.victim == gLocal_net_ID) {
  1619.         if (gCurrent_net_game->type == eNet_game_type_fight_to_death) {
  1620.             DoFancyHeadup(kFancyHeadupYouLost);
  1621.             gRace_finished = 1;
  1622.         } else {
  1623.             last_got_wasted_time = PDGetTotalTime();
  1624.             if (last_got_wasted_time - last_wasted_em_time > 1000) {
  1625.                 DoFancyHeadup(kFancyHeadupYouAreWasted);
  1626.             } else {
  1627.                 DoFancyHeadup(kFancyHeadupYouAreBothWasted);
  1628.             }
  1629.         }
  1630.     }
  1631.     if (pContents->data.wasted.culprit == -1) {
  1632.         if (victim->last_waste_message == 0) {
  1633.             victim->last_waste_message = GetTotalTime();
  1634.         }
  1635.     } else if (!victim->wasteage_attributed) {
  1636.         if (pContents->data.wasted.culprit == -2) {
  1637.             culprit = NULL;
  1638.         } else {
  1639.             culprit = NetPlayerFromID(pContents->data.wasted.culprit);
  1640.         }
  1641.         if (culprit != NULL && gNet_mode == eNet_mode_host && gCurrent_net_game->type == eNet_game_type_car_crusher) {
  1642.             culprit->score++;
  1643.             if (culprit->score >= gCurrent_net_game->options.race_end_target) {
  1644.                 DeclareWinner(culprit - gNet_players);
  1645.             }
  1646.         }
  1647.         victim->last_waste_message = GetTotalTime();
  1648.         victim->wasteage_attributed = 1;
  1649.         if (victim == last_culprit && culprit == last_victim && victim != NULL && culprit != NULL && PDGetTotalTime() - last_wasty_message_time < 1000) {
  1650.             sprintf(s, "%s %s %s %s", victim->player_name, GetMiscString(kMiscString_AND), culprit->player_name, GetMiscString(kMiscString_WASTED_EACH_OTHER));
  1651.         } else {
  1652.             sprintf(s, "%s %s %s", victim->player_name, GetMiscString(kMiscString_WastedBy), culprit ? culprit->player_name : GetMiscString(kMiscString_COP));
  1653.         }
  1654.         NewTextHeadupSlot2(4, 0, 3000, -4, s, 0);
  1655.         last_wasty_message_time = PDGetTotalTime();
  1656.         last_culprit = culprit;
  1657.         last_victim = victim;
  1658.         if (pContents->data.wasted.culprit == gLocal_net_ID) {
  1659.             PratcamEvent(32);
  1660.             last_wasted_em_time = PDGetTotalTime();
  1661.             if (last_wasted_em_time - last_got_wasted_time > 1000) {
  1662.                 DoFancyHeadup(kFancyHeadupYouWastedEm);
  1663.             } else {
  1664.                 DoFancyHeadup(kFancyHeadupYouAreBothWasted);
  1665.             }
  1666.         }
  1667.     }
  1668. }
  1669.  
  1670. // IDA: void __usercall ReceivedCarDetailsReq(tNet_contents *pContents@<EAX>, void *pSender_address@<EDX>)
  1671. void ReceivedCarDetailsReq(tNet_contents* pContents, void* pSender_address) {
  1672.     tNet_message* message;
  1673.     int i;
  1674.     LOG_TRACE("(%p, %p)", pContents, pSender_address);
  1675.  
  1676.     message = NetBuildMessage(NETMSGID_CARDETAILS, 0);
  1677.     message->contents.data.car_details.count = gNumber_of_net_players;
  1678.     for (i = 0; i < gNumber_of_net_players; i++) {
  1679.         message->contents.data.car_details.details[i].car_index = gNet_players[i].car_index;
  1680.         // truncates from 32 to 16 characters
  1681.         memcpy(message->contents.data.car_details.details[i].owner, gNet_players[i].player_name, sizeof(message->contents.data.car_details.details[i].owner));
  1682.         message->contents.data.car_details.details[i].owner[sizeof(message->contents.data.car_details.details[i].owner) - 1] = '\0';
  1683.     }
  1684.     NetGuaranteedSendMessageToAddress(gCurrent_net_game, message, pSender_address, NULL);
  1685. }
  1686.  
  1687. // IDA: void __usercall ReceivedCarDetails(tNet_contents *pContents@<EAX>)
  1688. void ReceivedCarDetails(tNet_contents* pContents) {
  1689.     int i;
  1690.     int j;
  1691.     LOG_TRACE("(%p)", pContents);
  1692.  
  1693.     SetNetAvailability(gNet_options);
  1694.     for (i = 0; i < gNumber_of_racers; i++) {
  1695.         for (j = 0; j < pContents->data.car_details.count; j++) {
  1696.             if (i == pContents->data.car_details.details[j].car_index) {
  1697.                 gCar_details[i].ownership = eCar_owner_someone;
  1698.                 strcpy(gCar_details[i].name, pContents->data.car_details.details[j].owner);
  1699.             }
  1700.         }
  1701.     }
  1702.     gReceived_car_details = 1;
  1703. }
  1704.  
  1705. // IDA: void __usercall ReceivedGameScores(tNet_contents *pContents@<EAX>)
  1706. void ReceivedGameScores(tNet_contents* pContents) {
  1707.     int i;
  1708.     LOG_TRACE("(%p)", pContents);
  1709.  
  1710.     gReceived_game_scores = 1;
  1711.     for (i = 0; i < gNumber_of_net_players; i++) {
  1712.         gNet_players[i].played = pContents->data.game_scores.scores[i].played;
  1713.         gNet_players[i].won = pContents->data.game_scores.scores[i].won;
  1714.         gNet_players[i].games_score = pContents->data.game_scores.scores[i].score;
  1715.     }
  1716. }
  1717.  
  1718. // IDA: void __usercall ReceivedMessage(tNet_message *pMessage@<EAX>, void *pSender_address@<EDX>, tU32 pReceive_time@<EBX>)
  1719. void ReceivedMessage(tNet_message* pMessage, void* pSender_address, tU32 pReceive_time) {
  1720.     tNet_contents* contents;
  1721.     int i;
  1722.     LOG_TRACE("(%p, %p, %d)", pMessage, pSender_address, pReceive_time);
  1723.  
  1724.     contents = &pMessage->contents;
  1725.     if (pMessage->guarantee_number != 0) {
  1726.         SendGuaranteeReply(pMessage, pSender_address);
  1727.     }
  1728.     if (!gProgram_state.racing && gRace_only_flags[pMessage->contents.header.type]) {
  1729.         return;
  1730.     }
  1731.     if (gOnly_receive_guarantee_replies && pMessage->contents.header.type != NETMSGID_GUARANTEEREPLY) {
  1732.         return;
  1733.     }
  1734.  
  1735.     for (i = 0; i < pMessage->num_contents; i++) {
  1736.         if (contents->header.type <= NETMSGID_CARDETAILS || PlayerIsInList(pMessage->sender)) {
  1737.             switch (contents->header.type) {
  1738.             case NETMSGID_SENDMEDETAILS: // 0x00,
  1739.                 ReceivedSendMeDetails(contents, pSender_address);
  1740.                 break;
  1741.             case NETMSGID_DETAILS: // 0x01,
  1742.                 ReceivedDetails(contents);
  1743.                 break;
  1744.             case NETMSGID_JOIN: // 0x02,
  1745.                 ReceivedJoin(contents, pSender_address);
  1746.                 break;
  1747.             case NETMSGID_NEWPLAYERLIST: // 0x03,
  1748.                 ReceivedNewPlayerList(contents, pMessage);
  1749.                 break;
  1750.             case NETMSGID_GUARANTEEREPLY: // 0x04,
  1751.                 ReceivedGuaranteeReply(contents);
  1752.                 break;
  1753.             case NETMSGID_CARDETAILSREQ: // 0x05,
  1754.                 ReceivedCarDetailsReq(contents, pSender_address);
  1755.                 break;
  1756.             case NETMSGID_CARDETAILS: // 0x06,
  1757.                 ReceivedCarDetails(contents);
  1758.                 break;
  1759.             case NETMSGID_LEAVE: // 0x07,
  1760.                 ReceivedLeave(contents, pMessage);
  1761.                 break;
  1762.             case NETMSGID_HOSTICIDE: // 0x08,
  1763.                 ReceivedHosticide(contents);
  1764.                 break;
  1765.             case NETMSGID_RACEOVER: // 0x09,
  1766.                 ReceivedRaceOver(contents);
  1767.                 break;
  1768.             case NETMSGID_STATUSREPORT: // 0x0a,
  1769.                 ReceivedStatusReport(contents, pMessage);
  1770.                 break;
  1771.             case NETMSGID_STARTRACE: // 0x0b,
  1772.                 ReceivedStartRace(contents);
  1773.                 break;
  1774.             case NETMSGID_HEADUP: // 0x0c,
  1775.                 ReceivedHeadup(contents);
  1776.                 break;
  1777.             case NETMSGID_HOSTQUERY: // 0x0d,
  1778.                 ReceivedHostQuery(contents, pMessage);
  1779.                 break;
  1780.             case NETMSGID_HOSTREPLY: // 0x0e,
  1781.                 ReceivedHostReply(contents);
  1782.                 break;
  1783.             case NETMSGID_MECHANICS: // 0x0f,
  1784.                 ReceivedMechanics(contents);
  1785.                 break;
  1786.             case NETMSGID_NONCAR_INFO: // 0x10,
  1787.                 ReceivedNonCar(contents);
  1788.                 break;
  1789.             case NETMSGID_TIMESYNC: // 0x11,
  1790.                 ReceivedTimeSync(contents, pMessage, pReceive_time);
  1791.                 break;
  1792.             case NETMSGID_CONFIRM: // 0x12,
  1793.                 ReceivedConfirm(contents);
  1794.                 break;
  1795.             case NETMSGID_DISABLECAR: // 0x13,
  1796.                 ReceivedDisableCar(contents);
  1797.                 break;
  1798.             case NETMSGID_ENABLECAR: // 0x14,
  1799.                 ReceivedEnableCar(contents);
  1800.                 break;
  1801.             case NETMSGID_POWERUP: // 0x15,
  1802.                 ReceivedPowerup(contents);
  1803.                 break;
  1804.             case NETMSGID_RECOVER: // 0x16,
  1805.                 ReceivedRecover(contents);
  1806.                 break;
  1807.             case NETMSGID_SCORES: // 0x17,
  1808.                 ReceivedScores(contents);
  1809.                 break;
  1810.             case NETMSGID_WASTED: // 0x18,
  1811.                 ReceivedWasted(contents);
  1812.                 break;
  1813.             case NETMSGID_PEDESTRIAN: // 0x19,
  1814.                 ReceivedPedestrian(contents, pMessage, pReceive_time);
  1815.                 break;
  1816.             case NETMSGID_GAMEPLAY: // 0x1a,
  1817.                 ReceivedGameplay(contents, pMessage, pReceive_time);
  1818.                 break;
  1819.             case NETMSGID_NONCARPOSITION: // 0x1b,
  1820.                 ReceivedNonCarPosition(contents);
  1821.                 break;
  1822.             case NETMSGID_COPINFO: // 0x1c,
  1823.                 ReceivedCopInfo(contents);
  1824.                 break;
  1825.             case NETMSGID_GAMESCORES: // 0x1d,
  1826.                 ReceivedGameScores(contents);
  1827.                 break;
  1828.             case NETMSGID_OILSPILL: // 0x1e,
  1829.                 ReceivedOilSpill(contents);
  1830.                 break;
  1831.             case NETMSGID_CRUSHPOINT: // 0x1f,
  1832.                 RecievedCrushPoint(contents);
  1833.                 break;
  1834.             }
  1835.         }
  1836.         contents = (tNet_contents*)((tU8*)contents + contents->header.contents_size);
  1837.     }
  1838. }
  1839.  
  1840. // IDA: void __cdecl NetReceiveAndProcessMessages()
  1841. void NetReceiveAndProcessMessages(void) {
  1842.     tNet_message* message;
  1843.     void* sender_address;
  1844.     tU32 receive_time;
  1845.     int old_net_service;
  1846.     LOG_TRACE("()");
  1847.  
  1848.     old_net_service = gIn_net_service;
  1849.     if (gNet_mode != eNet_mode_none || gJoin_list_mode) {
  1850.         gIn_net_service = 1;
  1851.         while ((message = NetGetNextMessage(gCurrent_net_game, &sender_address)) != NULL) {
  1852.             receive_time = GetRaceTime();
  1853.             if (message->magic_number == 0x763a5058) {
  1854.                 CheckCheckSum(message);
  1855.                 ReceivedMessage(message, sender_address, receive_time);
  1856.             } else {
  1857.                 message->guarantee_number = 0;
  1858.             }
  1859.             NetDisposeMessage(gCurrent_net_game, message);
  1860.         }
  1861.     }
  1862.     gIn_net_service = old_net_service;
  1863. }
  1864.  
  1865. // IDA: void __cdecl BroadcastStatus()
  1866. void BroadcastStatus(void) {
  1867.     tNet_message* message;
  1868.     LOG_TRACE("()");
  1869.  
  1870.     message = NetBuildMessage(NETMSGID_STATUSREPORT, 0);
  1871.     message->contents.data.report.status = gNet_players[gThis_net_player_index].player_status;
  1872.     NetSendMessageToAllPlayers(gCurrent_net_game, message);
  1873. }
  1874.  
  1875. // IDA: void __cdecl CheckForDisappearees()
  1876. void CheckForDisappearees(void) {
  1877.     int i;
  1878.     //int j; // Pierre-Marie Baty -- unused variable
  1879.     tU32 the_time;
  1880.     char s[256];
  1881.     //char* s2; // Pierre-Marie Baty -- unused variable
  1882.     LOG_TRACE("()");
  1883.  
  1884.     the_time = PDGetTotalTime();
  1885.     if (gNet_mode == eNet_mode_host) {
  1886.         for (i = 0; i < gNumber_of_net_players; i++) {
  1887.             if (!gNet_players[i].host && gNet_players[i].last_heard_from_him != 0 && the_time - gNet_players[i].last_heard_from_him >= 20000) {
  1888.                 strcpy(s, gNet_players[i].player_name);
  1889.                 strcat(s, " ");
  1890.                 strcat(s, GetMiscString(kMiscString_IS_NO_LONGER_RESPONDING));
  1891.                 NetSendHeadupToAllPlayers(s);
  1892.                 KickPlayerOut(gNet_players[i].ID);
  1893.                 if (gProgram_state.racing) {
  1894.                     NewTextHeadupSlot(4, 0, 3000, -4, s);
  1895.                 }
  1896.             }
  1897.         }
  1898.     } else if (!gHost_died && gNumber_of_net_players != 0 && gNet_players[0].last_heard_from_him != 0 && the_time - gNet_players[0].last_heard_from_him >= 20000) {
  1899.         HostHasBittenTheDust(kMiscString_PANIC_HOST_HAS_DISAPPEARED);
  1900.     }
  1901. }
  1902.  
  1903. // IDA: void __cdecl CheckForPendingStartRace()
  1904. void CheckForPendingStartRace(void) {
  1905.     int i;
  1906.     LOG_TRACE("()");
  1907.  
  1908.     if (gNet_mode == eNet_mode_host && gNeed_to_send_start_race) {
  1909.         for (i = 1; i < gNumber_of_net_players; i++) {
  1910.             if (gNet_players[i].awaiting_confirmation) {
  1911.                 return;
  1912.             }
  1913.         }
  1914.         SignalToStartRace();
  1915.     }
  1916. }
  1917.  
  1918. // IDA: void __usercall NetService(int pIn_race@<EAX>)
  1919. void NetService(int pIn_race) {
  1920.     tU32 time;
  1921.     static tU32 last_status_broadcast;
  1922.  
  1923.     if (gIn_net_service || gNet_service_disable) {
  1924.         return;
  1925.     }
  1926.     time = PDGetTotalTime();
  1927.     gIn_net_service = 1;
  1928.     if (gJoin_list_mode) {
  1929.         NetFreeExcessMemory();
  1930.         DoNextJoinPoll();
  1931.         NetReceiveAndProcessMessages();
  1932.     } else {
  1933.         if (gNet_mode != eNet_mode_none) {
  1934.             NetFreeExcessMemory();
  1935.             if (!pIn_race) {
  1936.                 NetReceiveAndProcessMessages();
  1937.             }
  1938.             if (time - last_status_broadcast > 1000) {
  1939.                 last_status_broadcast = PDGetTotalTime();
  1940.                 BroadcastStatus();
  1941.             }
  1942.             CheckForDisappearees();
  1943.             CheckForPendingStartRace();
  1944.         }
  1945.     }
  1946.     if (gJoin_list_mode || gNet_mode != eNet_mode_none) {
  1947.         ResendGuaranteedMessages();
  1948.         if (time > gLast_flush_message + 200) {
  1949.             NetSendMessageStacks();
  1950.         }
  1951.     }
  1952.     gIn_net_service = 0;
  1953. }
  1954.  
  1955. // IDA: void __usercall NetFinishRace(tNet_game_details *pDetails@<EAX>, tRace_over_reason pReason@<EDX>)
  1956. void NetFinishRace(tNet_game_details* pDetails, tRace_over_reason pReason) {
  1957.     tNet_message* the_message;
  1958.     LOG_TRACE("(%p, %d)", pDetails, pReason);
  1959.  
  1960.     gNeed_to_send_start_race = 0;
  1961.     the_message = NetBuildMessage(NETMSGID_RACEOVER, 0);
  1962.     the_message->contents.data.race_over.reason = pReason;
  1963.     NetGuaranteedSendMessageToAllPlayers(gCurrent_net_game, the_message, NULL);
  1964. }
  1965.  
  1966. // IDA: void __usercall NetPlayerStatusChanged(tPlayer_status pNew_status@<EAX>)
  1967. void NetPlayerStatusChanged(tPlayer_status pNew_status) {
  1968.     LOG_TRACE("(%d)", pNew_status);
  1969.     //tNet_message* the_message; // Pierre-Marie Baty -- unused variable
  1970.  
  1971.     if (gNet_mode != eNet_mode_none && pNew_status != gNet_players[gThis_net_player_index].player_status) {
  1972.         gNet_players[gThis_net_player_index].player_status = pNew_status;
  1973.         BroadcastStatus();
  1974.         if (gProgram_state.current_car.disabled && pNew_status >= ePlayer_status_racing && pNew_status != ePlayer_status_recovering) {
  1975.             EnableCar(&gProgram_state.current_car);
  1976.         } else if (!gProgram_state.current_car.disabled && pNew_status < ePlayer_status_racing) {
  1977.             DisableCar(&gProgram_state.current_car);
  1978.         }
  1979.     }
  1980. }
  1981.  
  1982. // IDA: tPlayer_status __cdecl NetGetPlayerStatus()
  1983. tPlayer_status NetGetPlayerStatus(void) {
  1984.     LOG_TRACE("()");
  1985.  
  1986.     return gNet_players[gThis_net_player_index].player_status;
  1987. }
  1988.  
  1989. // IDA: int __usercall NetGuaranteedSendMessageToAllPlayers@<EAX>(tNet_game_details *pDetails@<EAX>, tNet_message *pMessage@<EDX>, int (*pNotifyFail)(tU32, tNet_message*)@<EBX>)
  1990. int NetGuaranteedSendMessageToAllPlayers(tNet_game_details* pDetails, tNet_message* pMessage, int (*pNotifyFail)(tU32, tNet_message*)) {
  1991.     int i;
  1992.     int err;
  1993.     LOG_TRACE("(%p, %p, %p)", pDetails, pMessage, pNotifyFail);
  1994.  
  1995.     err = 0;
  1996.     if (gNumber_of_net_players == 1) {
  1997.         NetDisposeMessage(pDetails, pMessage);
  1998.         err = 0;
  1999.     } else {
  2000.         for (i = 0; i < gNumber_of_net_players; i++) {
  2001.             if (gThis_net_player_index != i) {
  2002.                 err |= NetGuaranteedSendMessageToAddress(pDetails, pMessage, &gNet_players[i], pNotifyFail);
  2003.             }
  2004.         }
  2005.     }
  2006.     return err;
  2007. }
  2008.  
  2009. // IDA: int __usercall NetGuaranteedSendMessageToEverybody@<EAX>(tNet_game_details *pDetails@<EAX>, tNet_message *pMessage@<EDX>, int (*pNotifyFail)(tU32, tNet_message*)@<EBX>)
  2010. int NetGuaranteedSendMessageToEverybody(tNet_game_details* pDetails, tNet_message* pMessage, int (*pNotifyFail)(tU32, tNet_message*)) {
  2011.     LOG_TRACE("(%p, %p, %p)", pDetails, pMessage, pNotifyFail);
  2012.  
  2013.     pMessage->sender = gLocal_net_ID;
  2014.     pMessage->senders_time_stamp = PDGetTotalTime();
  2015.     pMessage->num_contents = 1;
  2016.     pMessage->guarantee_number = 0;
  2017.     ReceivedMessage(pMessage, &gNet_players[gThis_net_player_index], GetRaceTime());
  2018.     return NetGuaranteedSendMessageToAllPlayers(pDetails, pMessage, pNotifyFail);
  2019. }
  2020.  
  2021. // IDA: int __usercall NetGuaranteedSendMessageToHost@<EAX>(tNet_game_details *pDetails@<EAX>, tNet_message *pMessage@<EDX>, int (*pNotifyFail)(tU32, tNet_message*)@<EBX>)
  2022. int NetGuaranteedSendMessageToHost(tNet_game_details* pDetails, tNet_message* pMessage, int (*pNotifyFail)(tU32, tNet_message*)) {
  2023.     LOG_TRACE("(%p, %p, %p)", pDetails, pMessage, pNotifyFail);
  2024.  
  2025.     return NetGuaranteedSendMessageToAddress(pDetails, pMessage, &pDetails->pd_net_info, pNotifyFail);
  2026. }
  2027.  
  2028. // IDA: int __usercall NetGuaranteedSendMessageToPlayer@<EAX>(tNet_game_details *pDetails@<EAX>, tNet_message *pMessage@<EDX>, tPlayer_ID pPlayer@<EBX>, int (*pNotifyFail)(tU32, tNet_message*)@<ECX>)
  2029. int NetGuaranteedSendMessageToPlayer(tNet_game_details* pDetails, tNet_message* pMessage, tPlayer_ID pPlayer, int (*pNotifyFail)(tU32, tNet_message*)) {
  2030.     int i;
  2031.     LOG_TRACE("(%p, %p, %d, %p)", pDetails, pMessage, pPlayer, pNotifyFail);
  2032.  
  2033.     for (i = 0; i <= gNumber_of_net_players; i++) {
  2034.         if (pPlayer == gNet_players[i].ID) {
  2035.             break;
  2036.         }
  2037.     }
  2038.     if (i == gNumber_of_net_players) {
  2039.         return -1;
  2040.     }
  2041.     if (gLocal_net_ID != pPlayer) {
  2042.         return NetGuaranteedSendMessageToAddress(pDetails, pMessage, &gNet_players[i].pd_net_info, pNotifyFail);
  2043.     }
  2044.     pMessage->sender = gLocal_net_ID;
  2045.     pMessage->senders_time_stamp = PDGetTotalTime();
  2046.     pMessage->num_contents = 1;
  2047.     pMessage->guarantee_number = 0;
  2048.     ReceivedMessage(pMessage, &gNet_players[i], GetRaceTime());
  2049.     NetDisposeMessage(pDetails, pMessage);
  2050.     return 0;
  2051. }
  2052.  
  2053. // IDA: int __usercall NetGuaranteedSendMessageToAddress@<EAX>(tNet_game_details *pDetails@<EAX>, tNet_message *pMessage@<EDX>, void *pAddress@<EBX>, int (*pNotifyFail)(tU32, tNet_message*)@<ECX>)
  2054. int NetGuaranteedSendMessageToAddress(tNet_game_details* pDetails, tNet_message* pMessage, void* pAddress, int (*pNotifyFail)(tU32, tNet_message*)) {
  2055.     char buffer[256]; // Added by Dethrace
  2056.     LOG_TRACE("(%p, %p, %p, %p)", pDetails, pMessage, pAddress, pNotifyFail);
  2057.  
  2058.     if (gNet_mode == eNet_mode_none && !gJoin_list_mode) {
  2059.         return -3;
  2060.     }
  2061.     pMessage->sender = gLocal_net_ID;
  2062.     pMessage->senders_time_stamp = PDGetTotalTime();
  2063.     if (gNext_guarantee >= COUNT_OF(gGuarantee_list)) {
  2064.         sprintf(buffer, "Guarantee list full %d", pMessage->contents.header.type);
  2065.         NewTextHeadupSlot(4, 0, 500, -1, buffer);
  2066.         pMessage->guarantee_number = 0;
  2067.         return 0;
  2068.     }
  2069.     pMessage->guarantee_number = gGuarantee_number;
  2070.     gGuarantee_list[gNext_guarantee].guarantee_number = gGuarantee_number;
  2071.     gGuarantee_number++;
  2072.     gGuarantee_list[gNext_guarantee].message = pMessage;
  2073.     gGuarantee_list[gNext_guarantee].send_time = PDGetTotalTime();
  2074.     gGuarantee_list[gNext_guarantee].next_resend_time = gGuarantee_list[gNext_guarantee].send_time + 100;
  2075.     gGuarantee_list[gNext_guarantee].resend_period = 100;
  2076.     memcpy(&gGuarantee_list[gNext_guarantee].pd_address, pAddress, sizeof(tPD_net_player_info));
  2077.     gGuarantee_list[gNext_guarantee].NotifyFail = pNotifyFail;
  2078.     gGuarantee_list[gNext_guarantee].recieved = 0;
  2079.     gNext_guarantee++;
  2080.     DoCheckSum(pMessage);
  2081.     return PDNetSendMessageToAddress(pDetails, pMessage, pAddress);
  2082. }
  2083.  
  2084. // IDA: void __cdecl ResendGuaranteedMessages()
  2085. void ResendGuaranteedMessages(void) {
  2086.     int i;
  2087.     int j;
  2088.     tU32 time;
  2089.     LOG_TRACE("()");
  2090.  
  2091.     i = 0;
  2092.     time = PDGetTotalTime();
  2093.     for (j = 0; j < gNext_guarantee; j++) {
  2094.         if (i != j) {
  2095.             memcpy(&gGuarantee_list[i], &gGuarantee_list[j], sizeof(tGuaranteed_message));
  2096.         }
  2097.         if (!gGuarantee_list[i].recieved) {
  2098.             if (gGuarantee_list[i].NotifyFail != NULL) {
  2099.                 gGuarantee_list[i].recieved |= gGuarantee_list[i].NotifyFail(time - gGuarantee_list[i].send_time, gGuarantee_list[i].message);
  2100.             } else {
  2101.                 if (time - gGuarantee_list[i].send_time > 10000) {
  2102.                     gGuarantee_list[i].recieved = 1;
  2103.                 }
  2104.             }
  2105.         }
  2106.         if (!gGuarantee_list[i].recieved) {
  2107.             if (time > gGuarantee_list[i].next_resend_time) {
  2108.                 gGuarantee_list[i].message->guarantee_number = gGuarantee_list[i].guarantee_number;
  2109.                 GetCheckSum(gGuarantee_list[i].message);
  2110.                 PDNetSendMessageToAddress(gCurrent_net_game, gGuarantee_list[i].message, &gGuarantee_list[i].pd_address);
  2111.                 gGuarantee_list[i].resend_period = (tU32)(gGuarantee_list[i].resend_period * 1.2f);
  2112.                 gGuarantee_list[i].next_resend_time += gGuarantee_list[i].resend_period;
  2113.             }
  2114.             i++;
  2115.         } else if ((i <= 0 || gGuarantee_list[i - 1].message != gGuarantee_list[i].message)
  2116.             && (gNext_guarantee <= j + 1 || gGuarantee_list[j + 1].message != gGuarantee_list[i].message)) {
  2117.             gGuarantee_list[i].message->guarantee_number = 0;
  2118.             NetDisposeMessage(gCurrent_net_game, gGuarantee_list[i].message);
  2119.         }
  2120.     }
  2121.     gNext_guarantee = i;
  2122. }
  2123.  
  2124. // IDA: int __usercall SampleFailNotifier@<EAX>(tU32 pAge@<EAX>, tNet_message *pMessage@<EDX>)
  2125. int SampleFailNotifier(tU32 pAge, tNet_message* pMessage) {
  2126.     LOG_TRACE("(%d, %p)", pAge, pMessage);
  2127.  
  2128.     return pAge > 9999;
  2129. }
  2130.  
  2131. // IDA: void __cdecl NetWaitForGuaranteeReplies()
  2132. void NetWaitForGuaranteeReplies(void) {
  2133.     tU32 start_time;
  2134.     LOG_TRACE("()");
  2135.  
  2136.     start_time = PDGetTotalTime();
  2137.     while (gNext_guarantee != 0) {
  2138.         if (PDGetTotalTime() - start_time >= 5000) {
  2139.             break;
  2140.         }
  2141.         NetService(0);
  2142.     }
  2143. }
  2144.  
  2145. // IDA: tNet_game_player_info* __usercall NetPlayerFromID@<EAX>(tPlayer_ID pPlayer@<EAX>)
  2146. tNet_game_player_info* NetPlayerFromID(tPlayer_ID pPlayer) {
  2147.     int i;
  2148.     LOG_TRACE("(%d)", pPlayer);
  2149.  
  2150.     for (i = 0; i < gNumber_of_net_players; i++) {
  2151.         if (gNet_players[i].ID == pPlayer) {
  2152.             return &gNet_players[i];
  2153.         }
  2154.     }
  2155.     return 0;
  2156. }
  2157.  
  2158. // IDA: tCar_spec* __usercall NetCarFromPlayerID@<EAX>(tPlayer_ID pPlayer@<EAX>)
  2159. tCar_spec* NetCarFromPlayerID(tPlayer_ID pPlayer) {
  2160.     int i;
  2161.     tNet_game_player_info* player;
  2162.     LOG_TRACE("(%d)", pPlayer);
  2163.  
  2164.     player = NetPlayerFromID(pPlayer);
  2165.     if (player) {
  2166.         return player->car;
  2167.     }
  2168.     return NULL;
  2169. }
  2170.  
  2171. // IDA: tNet_game_player_info* __usercall NetPlayerFromCar@<EAX>(tCar_spec *pCar@<EAX>)
  2172. tNet_game_player_info* NetPlayerFromCar(tCar_spec* pCar) {
  2173.     int i;
  2174.     LOG_TRACE("(%p)", pCar);
  2175.  
  2176.     for (i = 0; i < gNumber_of_net_players; i++) {
  2177.         if (gNet_players[i].car == pCar) {
  2178.             return &gNet_players[i];
  2179.         }
  2180.     }
  2181.     return 0;
  2182. }
  2183.  
  2184. // IDA: tU32 __usercall DoCheckSum@<EAX>(tNet_message *pMessage@<EAX>)
  2185. tU32 DoCheckSum(tNet_message* pMessage) {
  2186.     //int i; // Pierre-Marie Baty -- unused variable
  2187.     //int j; // Pierre-Marie Baty -- unused variable
  2188.     //tU32 the_sum; // Pierre-Marie Baty -- unused variable
  2189.     //tU32* p; // Pierre-Marie Baty -- unused variable
  2190.     //tU8* q; // Pierre-Marie Baty -- unused variable
  2191.     LOG_TRACE("(%p)", pMessage);
  2192.  
  2193.     // empty function
  2194.     return 0;
  2195. }
  2196.  
  2197. // IDA: void __usercall GetCheckSum(tNet_message *pMessage@<EAX>)
  2198. void GetCheckSum(tNet_message* pMessage) {
  2199.     LOG_TRACE("(%p)", pMessage);
  2200. }
  2201.  
  2202. // IDA: void __usercall CheckCheckSum(tNet_message *pMessage@<EAX>)
  2203. void CheckCheckSum(tNet_message* pMessage) {
  2204.     LOG_TRACE("(%p)", pMessage);
  2205. }
  2206.