From 4c519b85123ef4fd339ae2bd7ce5a87f0b8d1da3 Mon Sep 17 00:00:00 2001 From: Mark Olsen Date: Sun, 5 Mar 2023 07:49:29 +0000 Subject: [PATCH] Endian fixes for the Red Alert lobby networking code. --- common/connect.cpp | 2 + common/connect.h | 7 +++ redalert/ipxgconn.cpp | 8 ++- redalert/ipxgconn.h | 7 +++ redalert/ipxmgr.cpp | 31 +++++++++ redalert/ipxmgr.h | 4 ++ redalert/session.h | 136 +++++++++++++++++++++++++++++++++------ redalert/special.h | 143 +++++++++++++++++++++++++----------------- 8 files changed, 259 insertions(+), 79 deletions(-) diff --git a/common/connect.cpp b/common/connect.cpp index 7b8b8848..13b3386d 100644 --- a/common/connect.cpp +++ b/common/connect.cpp @@ -225,6 +225,8 @@ int ConnectionClass::Send_Packet(void* buf, int buflen, int ack_req) ((CommHeaderType*)PacketBuf)->PacketID = NumSendNoAck; } + SwapCommHeaderType((CommHeaderType*)PacketBuf); + /*------------------------------------------------------------------------ Now build the packet ------------------------------------------------------------------------*/ diff --git a/common/connect.h b/common/connect.h index 1a14f9d8..03dc2445 100644 --- a/common/connect.h +++ b/common/connect.h @@ -100,6 +100,7 @@ ********************************* Includes ********************************** */ #include "combuf.h" +#include "endianness.h" /* ********************************** Defines ********************************** @@ -122,6 +123,12 @@ typedef struct unsigned int PacketID; } CommHeaderType; +inline void SwapCommHeaderType(CommHeaderType* cht) +{ + cht->MagicNumber = le16toh(cht->MagicNumber); + cht->PacketID = le32toh(cht->PacketID); +} + /* ***************************** Class Declaration ***************************** */ diff --git a/redalert/ipxgconn.cpp b/redalert/ipxgconn.cpp index f6a09d6d..dcd0ee2a 100644 --- a/redalert/ipxgconn.cpp +++ b/redalert/ipxgconn.cpp @@ -143,6 +143,8 @@ int IPXGlobalConnClass::Send_Packet(void* buf, int buflen, IPXAddressClass* addr ------------------------------------------------------------------------*/ ((GlobalHeaderType*)PacketBuf)->ProductID = ProductID; + SwapGlobalHeaderType((GlobalHeaderType*)PacketBuf); + /*------------------------------------------------------------------------ Set this packet's destination address. If no address is specified, use a Broadcast address (which IPXAddressClass's default constructor creates). @@ -253,6 +255,7 @@ int IPXGlobalConnClass::Receive_Packet(void* buf, int buflen, IPXAddressClass* a ackpacket.Header.Code = PACKET_ACK; ackpacket.Header.PacketID = packet->Header.PacketID; ackpacket.ProductID = ProductID; + SwapGlobalHeaderType(&ackpacket); Send((char*)&ackpacket, sizeof(GlobalHeaderType), address, sizeof(IPXAddressClass)); } @@ -288,7 +291,8 @@ int IPXGlobalConnClass::Receive_Packet(void* buf, int buflen, IPXAddressClass* a /*............................................................... If ACK is for this entry, mark it ...............................................................*/ - if (packet->Header.PacketID == entry_data->Header.PacketID && entry_data->Header.Code == PACKET_DATA_ACK) { + if (packet->Header.PacketID == le32toh(entry_data->Header.PacketID) + && entry_data->Header.Code == PACKET_DATA_ACK) { send_entry->IsACK = 1; break; } @@ -361,7 +365,7 @@ int IPXGlobalConnClass::Get_Packet(void* buf, int* buflen, IPXAddressClass* addr memcpy(buf, rec_entry->Buffer + sizeof(GlobalHeaderType), packetlen); } (*buflen) = packetlen; - (*product_id) = packet->ProductID; + (*product_id) = le16toh(packet->ProductID); (*address) = (*((IPXAddressClass*)(rec_entry->ExtraBuffer))); return (1); diff --git a/redalert/ipxgconn.h b/redalert/ipxgconn.h index 49be3659..5172752e 100644 --- a/redalert/ipxgconn.h +++ b/redalert/ipxgconn.h @@ -75,6 +75,7 @@ #ifndef IPXGLOBALCONN_H #define IPXGLOBALCONN_H +#include "common/endianness.h" #include "ipxconn.h" /* @@ -94,6 +95,12 @@ typedef struct unsigned short ProductID; } GlobalHeaderType; +inline void SwapGlobalHeaderType(GlobalHeaderType* ght) +{ + SwapCommHeaderType(&ght->Header); + ght->ProductID = le16toh(ght->ProductID); +} + /* ***************************** Class Declaration ***************************** */ diff --git a/redalert/ipxmgr.cpp b/redalert/ipxmgr.cpp index 87fef43c..0a884594 100644 --- a/redalert/ipxmgr.cpp +++ b/redalert/ipxmgr.cpp @@ -739,6 +739,19 @@ int IPXManagerClass::Send_Global_Message(void* buf, int buflen, int ack_req, IPX } /* end of Send_Global_Message */ +int IPXManagerClass::Send_Global_Message(GlobalPacketType* buf, int buflen, int ack_req, IPXAddressClass* address) +{ + int rc; + + SwapGlobalPacketType(buf, true); + + rc = Send_Global_Message((void*)buf, buflen, ack_req, address); + + SwapGlobalPacketType(buf, false); + + return rc; +} /* end of Send_Global_Message */ + /*************************************************************************** * IPXManagerClass::Get_Global_Message -- polls the Global Message queue * * * @@ -769,6 +782,20 @@ int IPXManagerClass::Get_Global_Message(void* buf, int* buflen, IPXAddressClass* } /* end of Get_Global_Message */ +int IPXManagerClass::Get_Global_Message(GlobalPacketType* buf, + int* buflen, + IPXAddressClass* address, + unsigned short* product_id) +{ + int rc; + + rc = Get_Global_Message((void*)buf, buflen, address, product_id); + + SwapGlobalPacketType(buf, false); + + return rc; +} /* end of Get_Global_Message */ + /*************************************************************************** * IPXManagerClass::Send_Private_Message -- Sends a Private Message * * * @@ -962,6 +989,9 @@ int IPXManagerClass::Service(void) address = *((IPXAddressClass*)temp_address); packet = (CommHeaderType*)CurDataBuf; + + SwapCommHeaderType(packet); + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { /* @@ -1067,6 +1097,7 @@ int IPXManagerClass::Service(void) packet goes into the Global Queue, or into one of the Private Queues .....................................................................*/ packet = (CommHeaderType*)CurDataBuf; + SwapCommHeaderType(packet); if (packet->MagicNumber == GlobalChannel->Magic_Num()) { /*.................................................................. Put the packet in the Global Queue diff --git a/redalert/ipxmgr.h b/redalert/ipxmgr.h index 5e23f995..408a84f0 100644 --- a/redalert/ipxmgr.h +++ b/redalert/ipxmgr.h @@ -121,6 +121,8 @@ #include "common/ipxaddr.h" #include "connmgr.h" +struct GlobalPacketType; + /* ********************************** Defines ********************************** */ @@ -186,7 +188,9 @@ class IPXManagerClass : public ConnManClass This is how the application sends & receives messages. .....................................................................*/ int Send_Global_Message(void* buf, int buflen, int ack_req = 0, IPXAddressClass* address = NULL); + int Send_Global_Message(GlobalPacketType* buf, int buflen, int ack_req = 0, IPXAddressClass* address = NULL); int Get_Global_Message(void* buf, int* buflen, IPXAddressClass* address, unsigned short* product_id); + int Get_Global_Message(GlobalPacketType* buf, int* buflen, IPXAddressClass* address, unsigned short* product_id); virtual int Send_Private_Message(void* buf, int buflen, int ack_req = 1, int conn_id = CONNECTION_NONE); virtual int Get_Private_Message(void* buf, int* buflen, int* conn_id); diff --git a/redalert/session.h b/redalert/session.h index 6b1f5f00..e157e7dd 100644 --- a/redalert/session.h +++ b/redalert/session.h @@ -36,6 +36,7 @@ #include "common/ipxaddr.h" #include "common/bitfields.h" +#include "common/endianness.h" #include "msglist.h" #include "connect.h" #include "version.h" @@ -321,7 +322,7 @@ typedef struct #ifdef WOLAPI_INTEGRATION char ShortFileName[13]; // Name of scenario file to expect from host #else - char ShortFileName[12]; // Name of scenario file to expect from host + char ShortFileName[12]; // Name of scenario file to expect from host #endif unsigned char FileDigest[32]; // Digest of scenario file to expect from host // ajw - This is not necessarily null-terminated. @@ -361,7 +362,18 @@ typedef struct GlobalPacketType { struct BITFIELD_STRUCT { - unsigned int IsOpen : 1; // 1 = game is open for joining + union + { + unsigned int Bitfield; + + struct + { +#ifdef __BIG_ENDIAN__ + unsigned int Unused : 31; +#endif + unsigned int IsOpen : 1; // 1 = game is open for joining + }; + }; } GameInfo; struct { @@ -374,25 +386,42 @@ typedef struct GlobalPacketType } PlayerInfo; struct BITFIELD_STRUCT { - char Scenario[DESCRIP_MAX]; // Scenario Name - unsigned int Credits; // player's credits - unsigned int IsBases : 1; // 1 = bases are allowed - unsigned int IsTiberium : 1; // 1 = tiberium is allowed - unsigned int IsGoodies : 1; // 1 = goodies are allowed - unsigned int IsGhosties : 1; // 1 = ghosts are allowed - unsigned int OfficialScenario : 1; // Is this scenario an official Westwood one? - unsigned char BuildLevel; // buildable level - unsigned char UnitCount; // max # units - unsigned char AIPlayers; // # of AI players allowed - int Seed; // random number seed - SpecialClass Special; // command-line options - unsigned int GameSpeed; // Game Speed - unsigned int Version; // version # common to all players - unsigned int FileLength; // Length of scenario file to expect from host. + char Scenario[DESCRIP_MAX]; // Scenario Name + unsigned int Credits; // player's credits + union + { + unsigned int Bitfield; + + struct + { +#ifdef __BIG_ENDIAN__ + unsigned int Unused : 27; + unsigned int OfficialScenario : 1; // Is this scenario an official Westwood one? + unsigned int IsGhosties : 1; // 1 = ghosts are allowed + unsigned int IsGoodies : 1; // 1 = goodies are allowed + unsigned int IsTiberium : 1; // 1 = tiberium is allowed + unsigned int IsBases : 1; // 1 = bases are allowed +#else + unsigned int IsBases : 1; // 1 = bases are allowed + unsigned int IsTiberium : 1; // 1 = tiberium is allowed + unsigned int IsGoodies : 1; // 1 = goodies are allowed + unsigned int IsGhosties : 1; // 1 = ghosts are allowed + unsigned int OfficialScenario : 1; // Is this scenario an official Westwood one? +#endif + }; + }; + unsigned char BuildLevel; // buildable level + unsigned char UnitCount; // max # units + unsigned char AIPlayers; // # of AI players allowed + int Seed; // random number seed + SpecialClass Special; // command-line options + unsigned int GameSpeed; // Game Speed + unsigned int Version; // version # common to all players + unsigned int FileLength; // Length of scenario file to expect from host. #ifdef WOLAPI_INTEGRATION char ShortFileName[13]; // Name of scenario file to expect from host #else - char ShortFileName[12]; // Name of scenario file to expect from host + char ShortFileName[12]; // Name of scenario file to expect from host #endif unsigned char FileDigest[32]; // Digest of scenario file to expect from host // ajw - This is not necessarily null-terminated. @@ -419,6 +448,77 @@ typedef struct GlobalPacketType }; } GlobalPacketType; #pragma pack(pop) + +inline void SwapGlobalPacketType(GlobalPacketType* gpt, bool sending) +{ + enum NetCommandType command; + + if (sending) { + command = gpt->Command; + } else { + command = (enum NetCommandType)le32toh(gpt->Command); + } + + gpt->Command = (enum NetCommandType)le32toh(gpt->Command); + + switch (command) { + case NET_QUERY_GAME: + break; + + case NET_ANSWER_GAME: + gpt->GameInfo.Bitfield = le32toh(gpt->GameInfo.Bitfield); + break; + + case NET_QUERY_PLAYER: + break; + + case NET_ANSWER_PLAYER: + case NET_QUERY_JOIN: + case NET_CONFIRM_JOIN: + gpt->PlayerInfo.NameCRC = le32toh(gpt->PlayerInfo.NameCRC); + gpt->PlayerInfo.MinVersion = le32toh(gpt->PlayerInfo.MinVersion); + gpt->PlayerInfo.MaxVersion = le32toh(gpt->PlayerInfo.MaxVersion); + gpt->PlayerInfo.CheatCheck = le32toh(gpt->PlayerInfo.CheatCheck); + break; + + case NET_CHAT_ANNOUNCE: + gpt->Chat.ID = le32toh(gpt->Chat.ID); + break; + + case NET_CHAT_REQUEST: + break; + + case NET_REJECT_JOIN: + gpt->Reject.Why = le32toh(gpt->Reject.Why); + break; + + case NET_GAME_OPTIONS: + gpt->ScenarioInfo.Credits = le32toh(gpt->ScenarioInfo.Credits); + gpt->ScenarioInfo.Bitfield = le32toh(gpt->ScenarioInfo.Bitfield); + gpt->ScenarioInfo.Seed = le32toh(gpt->ScenarioInfo.Seed); + gpt->ScenarioInfo.Special.Bitfield = le32toh(gpt->ScenarioInfo.Special.Bitfield); + gpt->ScenarioInfo.GameSpeed = le32toh(gpt->ScenarioInfo.GameSpeed); + gpt->ScenarioInfo.Version = le32toh(gpt->ScenarioInfo.Version); + gpt->ScenarioInfo.FileLength = le32toh(gpt->ScenarioInfo.FileLength); + break; + + case NET_SIGN_OFF: + break; + + case NET_GO: + case NET_LOADGAME: + gpt->ResponseTime.OneWay = le32toh(gpt->ResponseTime.OneWay); + break; + + case NET_MESSAGE: + gpt->Message.NameCRC = le32toh(gpt->Message.NameCRC); + break; + + case NET_PING: + break; + } +} + //........................................................................... // For finding sync bugs; filled in by the engine when certain conditions // are met; the pointers allow examination of objects in the debugger. diff --git a/redalert/special.h b/redalert/special.h index 3f5fe03f..78dbeb8f 100644 --- a/redalert/special.h +++ b/redalert/special.h @@ -36,6 +36,7 @@ #define SPECIAL_H #include "common/bitfields.h" +#include "common/endianness.h" #pragma pack(push, 1) class BITFIELD_STRUCT SpecialClass @@ -47,65 +48,89 @@ class BITFIELD_STRUCT SpecialClass */ void Init(void); - /* - ** If the shroud should regenerated, then this flag will be true. - */ - unsigned IsShadowGrow : 1; - - /* - ** Controls the speedy build option -- used for testing. - */ - unsigned IsSpeedBuild : 1; - - /* - ** If from install, then play the special installation movie and - ** skip asking them what type of game they want to play. - */ - unsigned IsFromInstall : 1; - - /* - ** If capture the flag mode is on, this flag will be true. With this - ** flag enabled, then the flag is initially placed at the start of - ** the scenario. - */ - unsigned IsCaptureTheFlag : 1; - - /* - ** This flags controls whether weapons are inert. An inert weapon doesn't do any - ** damage. Effectively, if this is true, then the units never die. - */ - unsigned IsInert : 1; - - /* - ** If wheeled vehicles should do a 3-point turn when rotating in place, then - ** this flag is true. - */ - unsigned IsThreePoint : 1; - - /* - ** If Tiberium is allowed to spread and grow, then these flags will be true. - ** These are duplicated from the rules.ini file and also controlled by the - ** multiplayer dialog box. - */ - unsigned IsTGrowth : 1; - unsigned IsTSpread : 1; - - /* - ** If this flag is true, then the construction yard can undeploy back into an MCV. - ** Used to override the rules setting. - */ - unsigned UseMCVDeploy : 1; - unsigned IsMCVDeploy : 1; - - /* - ** New anti-griefing early win mode. ST - 1/31/2020 3:42PM - */ - unsigned IsEarlyWin : 1; - - /* - ** New modern balance setting. - */ - unsigned ModernBalance : 1; + union + { + unsigned Bitfield; + + struct + { +#ifdef __BIG_ENDIAN__ + unsigned int Unused : 20; + unsigned ModernBalance : 1; + unsigned IsEarlyWin : 1; + unsigned IsMCVDeploy : 1; + unsigned UseMCVDeploy : 1; + unsigned IsTSpread : 1; + unsigned IsTGrowth : 1; + unsigned IsThreePoint : 1; + unsigned IsInert : 1; + unsigned IsCaptureTheFlag : 1; + unsigned IsFromInstall : 1; + unsigned IsSpeedBuild : 1; + unsigned IsShadowGrow : 1; +#else + /* + ** If the shroud should regenerated, then this flag will be true. + */ + unsigned IsShadowGrow : 1; + + /* + ** Controls the speedy build option -- used for testing. + */ + unsigned IsSpeedBuild : 1; + + /* + ** If from install, then play the special installation movie and + ** skip asking them what type of game they want to play. + */ + unsigned IsFromInstall : 1; + + /* + ** If capture the flag mode is on, this flag will be true. With this + ** flag enabled, then the flag is initially placed at the start of + ** the scenario. + */ + unsigned IsCaptureTheFlag : 1; + + /* + ** This flags controls whether weapons are inert. An inert weapon doesn't do any + ** damage. Effectively, if this is true, then the units never die. + */ + unsigned IsInert : 1; + + /* + ** If wheeled vehicles should do a 3-point turn when rotating in place, then + ** this flag is true. + */ + unsigned IsThreePoint : 1; + + /* + ** If Tiberium is allowed to spread and grow, then these flags will be true. + ** These are duplicated from the rules.ini file and also controlled by the + ** multiplayer dialog box. + */ + unsigned IsTGrowth : 1; + unsigned IsTSpread : 1; + + /* + ** If this flag is true, then the construction yard can undeploy back into an MCV. + ** Used to override the rules setting. + */ + unsigned UseMCVDeploy : 1; + unsigned IsMCVDeploy : 1; + + /* + ** New anti-griefing early win mode. ST - 1/31/2020 3:42PM + */ + unsigned IsEarlyWin : 1; + + /* + ** New modern balance setting. + */ + unsigned ModernBalance : 1; +#endif + }; + }; }; #pragma pack(pop)