diff --git a/Source/DiabloUI/multi/selgame.cpp b/Source/DiabloUI/multi/selgame.cpp index 120ff7b4945..590465616ce 100644 --- a/Source/DiabloUI/multi/selgame.cpp +++ b/Source/DiabloUI/multi/selgame.cpp @@ -660,7 +660,9 @@ void selgame_Password_Select(int /*value*/) m_game_data->bTheoQuest = *sgOptions.Gameplay.theoQuest ? 1 : 0; m_game_data->bCowQuest = *sgOptions.Gameplay.cowQuest ? 1 : 0; - if (SNetCreateGame(nullptr, gamePassword, (char *)m_game_data, sizeof(*m_game_data), gdwPlayerId)) { + GameData gameInitInfo = *m_game_data; + gameInitInfo.swapLE(); + if (SNetCreateGame(nullptr, gamePassword, reinterpret_cast(&gameInitInfo), sizeof(gameInitInfo), gdwPlayerId)) { UiInitList_clear(); selgame_endMenu = true; } else { diff --git a/Source/dvlnet/base.cpp b/Source/dvlnet/base.cpp index 4e88c2dc26b..e7daa783246 100644 --- a/Source/dvlnet/base.cpp +++ b/Source/dvlnet/base.cpp @@ -94,7 +94,7 @@ void base::HandleDisconnect(packet &pkt) plr_t newPlayer = pkt.NewPlayer(); if (newPlayer != plr_self) { if (IsConnected(newPlayer)) { - auto leaveinfo = pkt.LeaveInfo(); + auto leaveinfo = SDL_SwapLE32(pkt.LeaveInfo()); _SNETEVENT ev; ev.eventid = EVENT_TYPE_PLAYER_LEAVE_GAME; ev.playerid = pkt.NewPlayer(); @@ -401,7 +401,7 @@ bool base::SNetRegisterEventHandler(event_type evtype, SEVTHANDLER func) bool base::SNetLeaveGame(int type) { auto pkt = pktfty->make_packet(plr_self, PLR_BROADCAST, - plr_self, type); + plr_self, static_cast(type)); send(*pkt); plr_self = PLR_BROADCAST; return true; @@ -411,8 +411,8 @@ bool base::SNetDropPlayer(int playerid, uint32_t flags) { auto pkt = pktfty->make_packet(plr_self, PLR_BROADCAST, - (plr_t)playerid, - (leaveinfo_t)flags); + static_cast(playerid), + static_cast(flags)); send(*pkt); RecvLocal(*pkt); return true; diff --git a/Source/dvlnet/base_protocol.h b/Source/dvlnet/base_protocol.h index a3b91e62c51..e75812d919b 100644 --- a/Source/dvlnet/base_protocol.h +++ b/Source/dvlnet/base_protocol.h @@ -296,8 +296,10 @@ void base_protocol

::recv_decrypted(packet &pkt, endpoint_t sender) size_t neededSize = sizeof(GameData) + (PlayerNameLength * MAX_PLRS); if (pkt.Info().size() < neededSize) return; - const GameData *gameData = (const GameData *)pkt.Info().data(); - if (gameData->size != sizeof(GameData)) + GameData gameData; + std::memcpy(&gameData, pkt.Info().data(), sizeof(GameData)); + gameData.swapLE(); + if (gameData.size != sizeof(GameData)) return; std::vector playerNames; for (size_t i = 0; i < Players.size(); i++) { @@ -316,7 +318,7 @@ void base_protocol

::recv_decrypted(packet &pkt, endpoint_t sender) size_t gameNameSize = pkt.Info().size() - neededSize; gameName.resize(gameNameSize); std::memcpy(&gameName[0], pkt.Info().data() + neededSize, gameNameSize); - game_list[gameName] = std::make_tuple(*gameData, playerNames, sender); + game_list[gameName] = std::make_tuple(gameData, playerNames, sender); return; } recv_ingame(pkt, sender); diff --git a/Source/dvlnet/frame_queue.cpp b/Source/dvlnet/frame_queue.cpp index 73e321c01e8..4c7ed3c51a4 100644 --- a/Source/dvlnet/frame_queue.cpp +++ b/Source/dvlnet/frame_queue.cpp @@ -5,6 +5,7 @@ #include "appfat.h" #include "dvlnet/packet.h" #include "utils/attributes.h" +#include "utils/endian.hpp" namespace devilution { namespace net { @@ -56,7 +57,7 @@ bool frame_queue::PacketReady() if (Size() < sizeof(framesize_t)) return false; auto szbuf = Read(sizeof(framesize_t)); - std::memcpy(&nextsize, &szbuf[0], sizeof(framesize_t)); + nextsize = LoadLE32(szbuf.data()); if (nextsize == 0) FRAME_QUEUE_ERROR; } @@ -77,8 +78,11 @@ buffer_t frame_queue::MakeFrame(buffer_t packetbuf) buffer_t ret; if (packetbuf.size() > max_frame_size) ABORT(); - framesize_t size = packetbuf.size(); - ret.insert(ret.end(), packet_out::begin(size), packet_out::end(size)); + const framesize_t size = packetbuf.size(); + static_assert(sizeof(size) == 4, "framesize_t is not 4 bytes"); + unsigned char sizeBuf[4]; + WriteLE32(sizeBuf, size); + ret.insert(ret.end(), sizeBuf, sizeBuf + 4); ret.insert(ret.end(), packetbuf.begin(), packetbuf.end()); return ret; } diff --git a/Source/dvlnet/packet.h b/Source/dvlnet/packet.h index a306a422fe5..fc725dc2126 100644 --- a/Source/dvlnet/packet.h +++ b/Source/dvlnet/packet.h @@ -5,6 +5,7 @@ #include #include #include +#include #ifdef PACKET_ENCRYPTION #include @@ -40,7 +41,7 @@ typedef uint8_t plr_t; typedef uint8_t seq_t; typedef uint32_t cookie_t; typedef uint32_t timestamp_t; -typedef int leaveinfo_t; // also change later +typedef uint32_t leaveinfo_t; #ifdef PACKET_ENCRYPTION typedef std::array key_t; #else @@ -140,11 +141,7 @@ class packet_out : public packet_proc { void process_element(buffer_t &x); template - void process_element(T &x); - template - static const unsigned char *begin(const T &x); - template - static const unsigned char *end(const T &x); + void process_element(const T &x); static cookie_t GenerateCookie(); void Encrypt(); }; @@ -202,13 +199,21 @@ inline void packet_in::process_element(buffer_t &x) template void packet_in::process_element(T &x) { + static_assert(std::is_integral::value || std::is_enum::value, "Unsupported T"); + static_assert(sizeof(T) == 4 || sizeof(T) == 2 || sizeof(T) == 1, "Unsupported T"); if (decrypted_buffer.size() < sizeof(T)) #if DVL_EXCEPTIONS throw packet_exception(); #else app_fatal("invalid packet"); #endif - std::memcpy(&x, decrypted_buffer.data(), sizeof(T)); + if (sizeof(T) == 4) { + x = static_cast(LoadLE32(decrypted_buffer.data())); + } else if (sizeof(T) == 2) { + x = static_cast(LoadLE16(decrypted_buffer.data())); + } else if (sizeof(T) == 1) { + std::memcpy(&x, decrypted_buffer.data(), sizeof(T)); + } decrypted_buffer.erase(decrypted_buffer.begin(), decrypted_buffer.begin() + sizeof(T)); } @@ -358,21 +363,21 @@ inline void packet_out::process_element(buffer_t &x) } template -void packet_out::process_element(T &x) +void packet_out::process_element(const T &x) { - decrypted_buffer.insert(decrypted_buffer.end(), begin(x), end(x)); -} - -template -const unsigned char *packet_out::begin(const T &x) -{ - return reinterpret_cast(&x); -} - -template -const unsigned char *packet_out::end(const T &x) -{ - return reinterpret_cast(&x) + sizeof(T); + static_assert(std::is_integral::value || std::is_enum::value, "Unsupported T"); + static_assert(sizeof(T) == 4 || sizeof(T) == 2 || sizeof(T) == 1, "Unsupported T"); + if (sizeof(T) == 4) { + unsigned char buf[4]; + WriteLE32(buf, x); + decrypted_buffer.insert(decrypted_buffer.end(), buf, buf + 4); + } else if (sizeof(T) == 2) { + unsigned char buf[2]; + WriteLE16(buf, x); + decrypted_buffer.insert(decrypted_buffer.end(), buf, buf + 2); + } else if (sizeof(T) == 1) { + decrypted_buffer.push_back(static_cast(x)); + } } class packet_factory { diff --git a/Source/dvlnet/tcp_server.cpp b/Source/dvlnet/tcp_server.cpp index 6cdb8b1cef8..65277c4a9eb 100644 --- a/Source/dvlnet/tcp_server.cpp +++ b/Source/dvlnet/tcp_server.cpp @@ -214,7 +214,7 @@ void tcp_server::DropConnection(const scc &con) { if (con->plr != PLR_BROADCAST) { auto pkt = pktfty.make_packet(PLR_MASTER, PLR_BROADCAST, - con->plr, LEAVE_DROP); + con->plr, static_cast(LEAVE_DROP)); connections[con->plr] = nullptr; SendPacket(*pkt); // TODO: investigate if it is really ok for the server to diff --git a/Source/multi.cpp b/Source/multi.cpp index 32452f5851b..dcc9e10c2c3 100644 --- a/Source/multi.cpp +++ b/Source/multi.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include @@ -46,7 +47,7 @@ bool sgbSendDeltaTbl[MAX_PLRS]; GameData sgGameInitInfo; bool gbSelectProvider; int sglTimeoutStart; -int sgdwPlayerLeftReasonTbl[MAX_PLRS]; +uint32_t sgdwPlayerLeftReasonTbl[MAX_PLRS]; TBuffer lowPriorityBuffer; uint32_t sgdwGameLoops; /** @@ -74,6 +75,13 @@ const event_type EventTypes[3] = { EVENT_TYPE_PLAYER_MESSAGE }; +void GameData::swapLE() +{ + size = SDL_SwapLE32(size); + dwSeed = SDL_SwapLE32(dwSeed); + programid = SDL_SwapLE32(programid); +} + namespace { constexpr uint16_t HeaderCheckVal = @@ -376,34 +384,44 @@ void SetupLocalPositions() void HandleEvents(_SNETEVENT *pEvt) { + const uint32_t playerId = pEvt->playerid; switch (pEvt->eventid) { case EVENT_TYPE_PLAYER_CREATE_GAME: { - auto *gameData = (GameData *)pEvt->data; - if (gameData->size != sizeof(GameData)) - app_fatal(StrCat("Invalid size of game data: ", gameData->size)); - sgGameInitInfo = *gameData; - sgbPlayerTurnBitTbl[pEvt->playerid] = true; - break; - } + GameData gameData; + if (pEvt->databytes < sizeof(GameData)) + app_fatal(StrCat("Invalid packet size (databytes)); + std::memcpy(&gameData, pEvt->data, sizeof(gameData)); + gameData.swapLE(); + if (gameData.size != sizeof(GameData)) + app_fatal(StrCat("Invalid size of game data: ", gameData.size)); + sgGameInitInfo = gameData; + sgbPlayerTurnBitTbl[playerId] = true; + } break; case EVENT_TYPE_PLAYER_LEAVE_GAME: { - sgbPlayerLeftGameTbl[pEvt->playerid] = true; - sgbPlayerTurnBitTbl[pEvt->playerid] = false; + sgbPlayerLeftGameTbl[playerId] = true; + sgbPlayerTurnBitTbl[playerId] = false; - int leftReason = 0; - if (pEvt->data != nullptr && pEvt->databytes >= sizeof(leftReason)) - leftReason = *(int *)pEvt->data; - sgdwPlayerLeftReasonTbl[pEvt->playerid] = leftReason; + int32_t leftReason = 0; + if (pEvt->data != nullptr && pEvt->databytes >= sizeof(leftReason)) { + std::memcpy(&leftReason, pEvt->data, sizeof(leftReason)); + leftReason = SDL_SwapLE32(leftReason); + } + sgdwPlayerLeftReasonTbl[playerId] = leftReason; if (leftReason == LEAVE_ENDING) gbSomebodyWonGameKludge = true; - sgbSendDeltaTbl[pEvt->playerid] = false; + sgbSendDeltaTbl[playerId] = false; - if (gbDeltaSender == pEvt->playerid) + if (gbDeltaSender == playerId) gbDeltaSender = MAX_PLRS; } break; - case EVENT_TYPE_PLAYER_MESSAGE: - EventPlrMsg((char *)pEvt->data); - break; + case EVENT_TYPE_PLAYER_MESSAGE: { + string_view data(static_cast(pEvt->data), pEvt->databytes); + if (const size_t nullPos = data.find('\0'); nullPos != string_view::npos) { + data.remove_suffix(data.size() - nullPos); + } + EventPlrMsg(data); + } break; } } @@ -432,7 +450,9 @@ bool InitSingle(GameData *gameData) } int unused = 0; - if (!SNetCreateGame("local", "local", (char *)&sgGameInitInfo, sizeof(sgGameInitInfo), &unused)) { + GameData gameInitInfo = sgGameInitInfo; + gameInitInfo.swapLE(); + if (!SNetCreateGame("local", "local", reinterpret_cast(&gameInitInfo), sizeof(gameInitInfo), &unused)) { app_fatal(StrCat("SNetCreateGame1:\n", SDL_GetError())); } diff --git a/Source/multi.h b/Source/multi.h index 0772828ee03..d0d4a2c6fe3 100644 --- a/Source/multi.h +++ b/Source/multi.h @@ -32,6 +32,8 @@ struct GameData { uint8_t bCowQuest; uint8_t bFriendlyFire; uint8_t fullQuests; + + void swapLE(); }; /* @brief Contains info of running public game (for game list browsing) */ diff --git a/Source/storm/storm_net.hpp b/Source/storm/storm_net.hpp index 22a6ed81a7b..9a9d95b1aea 100644 --- a/Source/storm/storm_net.hpp +++ b/Source/storm/storm_net.hpp @@ -36,10 +36,10 @@ struct _SNETCAPS { }; struct _SNETEVENT { - uint32_t eventid; - uint32_t playerid; - void *data; - uint32_t databytes; + uint32_t eventid; // native-endian + uint32_t playerid; // native-endian + void *data; // little-endian + uint32_t databytes; // native-endian }; #define PS_CONNECTED 0x10000