From 4c1dbaba72d399be23561b25c2d1182a56fcd258 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Mon, 18 Jul 2022 09:47:57 +0100 Subject: [PATCH] Demo mode: Handle endianness --- Source/debug.cpp | 9 +--- Source/engine/demomode.cpp | 82 +++++++++++++--------------------- Source/utils/endian.hpp | 27 ++++++++--- Source/utils/endian_stream.hpp | 78 ++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 65 deletions(-) create mode 100644 Source/utils/endian_stream.hpp diff --git a/Source/debug.cpp b/Source/debug.cpp index 3322a3321..b5db16209 100644 --- a/Source/debug.cpp +++ b/Source/debug.cpp @@ -27,6 +27,7 @@ #include "quests.h" #include "spells.h" #include "towners.h" +#include "utils/endian_stream.hpp" #include "utils/language.h" #include "utils/log.hpp" #include "utils/str_cat.hpp" @@ -288,14 +289,6 @@ std::string DebugCmdLoadMap(const string_view parameter) return "Welcome to this unique place."; } -void WriteLE16(std::ofstream &out, uint16_t val) -{ - const uint16_t littleEndian = SDL_SwapLE16(val); - char data[2]; - memcpy(data, &littleEndian, 2); - out.write(data, 2); -} - std::string ExportDun(const string_view parameter) { std::ofstream dunFile; diff --git a/Source/engine/demomode.cpp b/Source/engine/demomode.cpp index b5f0abb82..59fbb2649 100644 --- a/Source/engine/demomode.cpp +++ b/Source/engine/demomode.cpp @@ -11,6 +11,7 @@ #include "options.h" #include "pfile.h" #include "utils/display.h" +#include "utils/endian_stream.hpp" #include "utils/paths.h" #include "utils/str_cat.hpp" @@ -24,11 +25,11 @@ enum class DemoMsgType { Message = 2, }; -struct demoMsg { +struct DemoMsg { DemoMsgType type; uint32_t message; - int32_t wParam; - int32_t lParam; + uint32_t wParam; + uint32_t lParam; float progressToNextGameTick; }; @@ -38,7 +39,7 @@ int RecordNumber = -1; bool CreateDemoReference = false; std::ofstream DemoRecording; -std::deque Demo_Message_Queue; +std::deque Demo_Message_Queue; uint32_t DemoModeLastTick = 0; int LogicTick = 0; @@ -47,30 +48,9 @@ int StartTime = 0; uint16_t DemoGraphicsWidth = 640; uint16_t DemoGraphicsHeight = 480; -void PumpDemoMessage(DemoMsgType demoMsgType, uint32_t message, int32_t wParam, int32_t lParam, float progressToNextGameTick) +void PumpDemoMessage(DemoMsgType demoMsgType, uint32_t message, uint32_t wParam, uint32_t lParam, float progressToNextGameTick) { - demoMsg msg; - msg.type = demoMsgType; - msg.message = message; - msg.wParam = wParam; - msg.lParam = lParam; - msg.progressToNextGameTick = progressToNextGameTick; - - Demo_Message_Queue.push_back(msg); -} - -template -T ReadFromStream(std::ifstream &stream) -{ - T value; - stream.read(reinterpret_cast(&value), sizeof(value)); - return value; -} - -template -void WriteToDemo(T value) -{ - DemoRecording.write(reinterpret_cast(&value), sizeof(value)); + Demo_Message_Queue.push_back(DemoMsg { demoMsgType, message, wParam, lParam, progressToNextGameTick }); } bool LoadDemoMessages(int i) @@ -81,26 +61,26 @@ bool LoadDemoMessages(int i) return false; } - uint8_t version = ReadFromStream(demofile); + const uint8_t version = ReadByte(demofile); if (version != 0) { return false; } - gSaveNumber = ReadFromStream(demofile); - DemoGraphicsWidth = ReadFromStream(demofile); - DemoGraphicsHeight = ReadFromStream(demofile); + gSaveNumber = ReadLE32(demofile); + DemoGraphicsWidth = ReadLE16(demofile); + DemoGraphicsHeight = ReadLE16(demofile); while (!demofile.eof()) { - uint32_t typeNum = ReadFromStream(demofile); - auto type = static_cast(typeNum); + const uint32_t typeNum = ReadLE32(demofile); + const auto type = static_cast(typeNum); - float progressToNextGameTick = ReadFromStream(demofile); + const float progressToNextGameTick = ReadLEFloat(demofile); switch (type) { case DemoMsgType::Message: { - uint32_t message = ReadFromStream(demofile); - int32_t wParam = ReadFromStream(demofile); - int32_t lParam = ReadFromStream(demofile); + const uint32_t message = ReadLE32(demofile); + const uint32_t wParam = ReadLE32(demofile); + const uint32_t lParam = ReadLE32(demofile); PumpDemoMessage(type, message, wParam, lParam, progressToNextGameTick); break; } @@ -167,7 +147,7 @@ bool GetRunGameLoop(bool &drawGame, bool &processInput) { if (Demo_Message_Queue.empty()) app_fatal("Demo queue empty"); - demoMsg dmsg = Demo_Message_Queue.front(); + const DemoMsg dmsg = Demo_Message_Queue.front(); if (dmsg.type == DemoMsgType::Message) app_fatal("Unexpected Message"); if (Timedemo) { @@ -230,7 +210,7 @@ bool FetchMessage(tagMSG *lpMsg) } if (!Demo_Message_Queue.empty()) { - demoMsg dmsg = Demo_Message_Queue.front(); + const DemoMsg dmsg = Demo_Message_Queue.front(); if (dmsg.type == DemoMsgType::Message) { lpMsg->message = dmsg.message; lpMsg->lParam = dmsg.lParam; @@ -250,30 +230,30 @@ bool FetchMessage(tagMSG *lpMsg) void RecordGameLoopResult(bool runGameLoop) { - WriteToDemo(static_cast(runGameLoop ? DemoMsgType::GameTick : DemoMsgType::Rendering)); - WriteToDemo(gfProgressToNextGameTick); + WriteLE32(DemoRecording, static_cast(runGameLoop ? DemoMsgType::GameTick : DemoMsgType::Rendering)); + WriteLEFloat(DemoRecording, gfProgressToNextGameTick); } void RecordMessage(tagMSG *lpMsg) { if (!gbRunGame || !DemoRecording.is_open()) return; - WriteToDemo(static_cast(DemoMsgType::Message)); - WriteToDemo(gfProgressToNextGameTick); - WriteToDemo(lpMsg->message); - WriteToDemo(lpMsg->wParam); - WriteToDemo(lpMsg->lParam); + WriteLE32(DemoRecording, static_cast(DemoMsgType::Message)); + WriteLEFloat(DemoRecording, gfProgressToNextGameTick); + WriteLE32(DemoRecording, lpMsg->message); + WriteLE32(DemoRecording, lpMsg->wParam); + WriteLE32(DemoRecording, lpMsg->lParam); } void NotifyGameLoopStart() { if (IsRecording()) { DemoRecording.open(StrCat(paths::PrefPath(), "demo_", RecordNumber, ".dmo"), std::fstream::trunc | std::fstream::binary); - constexpr uint8_t version = 0; - WriteToDemo(version); - WriteToDemo(gSaveNumber); - WriteToDemo(gnScreenWidth); - WriteToDemo(gnScreenHeight); + constexpr uint8_t Version = 0; + WriteByte(DemoRecording, Version); + WriteLE32(DemoRecording, gSaveNumber); + WriteLE16(DemoRecording, gnScreenWidth); + WriteLE16(DemoRecording, gnScreenHeight); } if (IsRunning()) { diff --git a/Source/utils/endian.hpp b/Source/utils/endian.hpp index 8e38d9ec1..05a167c45 100644 --- a/Source/utils/endian.hpp +++ b/Source/utils/endian.hpp @@ -1,31 +1,46 @@ #pragma once #include +#include + +#include namespace devilution { template -constexpr std::uint16_t LoadLE16(const T *b) +constexpr uint16_t LoadLE16(const T *b) { static_assert(sizeof(T) == 1, "invalid argument"); // NOLINTNEXTLINE(readability-magic-numbers) - return (static_cast(b[1]) << 8) | static_cast(b[0]); + return (static_cast(b[1]) << 8) | static_cast(b[0]); } template -constexpr std::uint32_t LoadLE32(const T *b) +constexpr uint32_t LoadLE32(const T *b) { static_assert(sizeof(T) == 1, "invalid argument"); // NOLINTNEXTLINE(readability-magic-numbers) - return (static_cast(b[3]) << 24) | (static_cast(b[2]) << 16) | (static_cast(b[1]) << 8) | static_cast(b[0]); + return (static_cast(b[3]) << 24) | (static_cast(b[2]) << 16) | (static_cast(b[1]) << 8) | static_cast(b[0]); } template -constexpr std::uint32_t LoadBE32(const T *b) +constexpr uint32_t LoadBE32(const T *b) { static_assert(sizeof(T) == 1, "invalid argument"); // NOLINTNEXTLINE(readability-magic-numbers) - return (static_cast(b[0]) << 24) | (static_cast(b[1]) << 16) | (static_cast(b[2]) << 8) | static_cast(b[3]); + return (static_cast(b[0]) << 24) | (static_cast(b[1]) << 16) | (static_cast(b[2]) << 8) | static_cast(b[3]); +} + +inline void WriteLE16(void *out, uint16_t val) +{ + const uint16_t littleEndian = SDL_SwapLE16(val); + memcpy(out, &littleEndian, 2); +} + +inline void WriteLE32(void *out, uint32_t val) +{ + const uint32_t littleEndian = SDL_SwapLE32(val); + memcpy(out, &littleEndian, 4); } } // namespace devilution diff --git a/Source/utils/endian_stream.hpp b/Source/utils/endian_stream.hpp new file mode 100644 index 000000000..8545542b5 --- /dev/null +++ b/Source/utils/endian_stream.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include + +#include + +#include + +#include "utils/endian.hpp" + +namespace devilution { + +template +T ReadByte(std::ifstream &stream) +{ + static_assert(sizeof(T) == 1, "invalid argument"); + char buf; + stream.read(&buf, 1); + return static_cast(buf); +} + +template +T ReadLE16(std::ifstream &stream) +{ + static_assert(sizeof(T) == 2, "invalid argument"); + char buf[2]; + stream.read(buf, 2); + return static_cast(LoadLE16(buf)); +} + +template +T ReadLE32(std::ifstream &stream) +{ + static_assert(sizeof(T) == 4, "invalid argument"); + char buf[4]; + stream.read(buf, 4); + return static_cast(LoadLE32(buf)); +} + +inline float ReadLEFloat(std::ifstream &stream) +{ + static_assert(sizeof(float) == sizeof(uint32_t), "invalid float size"); + const uint32_t val = ReadLE32(stream); + float result; + memcpy(&result, &val, sizeof(float)); + return result; +} + +inline void WriteByte(std::ofstream &out, uint8_t val) +{ + out.write(reinterpret_cast(&val), 1); +} + +inline void WriteLE16(std::ofstream &out, uint16_t val) +{ + char data[2]; + WriteLE16(data, val); + out.write(data, 2); +} + +inline void WriteLE32(std::ofstream &out, uint32_t val) +{ + char data[4]; + WriteLE32(data, val); + out.write(data, 4); +} + +inline void WriteLEFloat(std::ofstream &out, float val) +{ + static_assert(sizeof(float) == sizeof(uint32_t), "invalid float size"); + uint32_t intVal; + memcpy(&intVal, &val, sizeof(uint32_t)); + char data[4]; + WriteLE32(data, intVal); + out.write(data, 4); +} + +} // namespace devilution