Browse Source

Demo mode: Handle endianness

pull/5047/head
Gleb Mazovetskiy 4 years ago
parent
commit
4c1dbaba72
  1. 9
      Source/debug.cpp
  2. 82
      Source/engine/demomode.cpp
  3. 27
      Source/utils/endian.hpp
  4. 78
      Source/utils/endian_stream.hpp

9
Source/debug.cpp

@ -27,6 +27,7 @@
#include "quests.h" #include "quests.h"
#include "spells.h" #include "spells.h"
#include "towners.h" #include "towners.h"
#include "utils/endian_stream.hpp"
#include "utils/language.h" #include "utils/language.h"
#include "utils/log.hpp" #include "utils/log.hpp"
#include "utils/str_cat.hpp" #include "utils/str_cat.hpp"
@ -288,14 +289,6 @@ std::string DebugCmdLoadMap(const string_view parameter)
return "Welcome to this unique place."; 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::string ExportDun(const string_view parameter)
{ {
std::ofstream dunFile; std::ofstream dunFile;

82
Source/engine/demomode.cpp

@ -11,6 +11,7 @@
#include "options.h" #include "options.h"
#include "pfile.h" #include "pfile.h"
#include "utils/display.h" #include "utils/display.h"
#include "utils/endian_stream.hpp"
#include "utils/paths.h" #include "utils/paths.h"
#include "utils/str_cat.hpp" #include "utils/str_cat.hpp"
@ -24,11 +25,11 @@ enum class DemoMsgType {
Message = 2, Message = 2,
}; };
struct demoMsg { struct DemoMsg {
DemoMsgType type; DemoMsgType type;
uint32_t message; uint32_t message;
int32_t wParam; uint32_t wParam;
int32_t lParam; uint32_t lParam;
float progressToNextGameTick; float progressToNextGameTick;
}; };
@ -38,7 +39,7 @@ int RecordNumber = -1;
bool CreateDemoReference = false; bool CreateDemoReference = false;
std::ofstream DemoRecording; std::ofstream DemoRecording;
std::deque<demoMsg> Demo_Message_Queue; std::deque<DemoMsg> Demo_Message_Queue;
uint32_t DemoModeLastTick = 0; uint32_t DemoModeLastTick = 0;
int LogicTick = 0; int LogicTick = 0;
@ -47,30 +48,9 @@ int StartTime = 0;
uint16_t DemoGraphicsWidth = 640; uint16_t DemoGraphicsWidth = 640;
uint16_t DemoGraphicsHeight = 480; 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; Demo_Message_Queue.push_back(DemoMsg { demoMsgType, message, wParam, lParam, progressToNextGameTick });
msg.type = demoMsgType;
msg.message = message;
msg.wParam = wParam;
msg.lParam = lParam;
msg.progressToNextGameTick = progressToNextGameTick;
Demo_Message_Queue.push_back(msg);
}
template <class T>
T ReadFromStream(std::ifstream &stream)
{
T value;
stream.read(reinterpret_cast<char *>(&value), sizeof(value));
return value;
}
template <class T>
void WriteToDemo(T value)
{
DemoRecording.write(reinterpret_cast<char *>(&value), sizeof(value));
} }
bool LoadDemoMessages(int i) bool LoadDemoMessages(int i)
@ -81,26 +61,26 @@ bool LoadDemoMessages(int i)
return false; return false;
} }
uint8_t version = ReadFromStream<uint8_t>(demofile); const uint8_t version = ReadByte(demofile);
if (version != 0) { if (version != 0) {
return false; return false;
} }
gSaveNumber = ReadFromStream<uint32_t>(demofile); gSaveNumber = ReadLE32(demofile);
DemoGraphicsWidth = ReadFromStream<uint16_t>(demofile); DemoGraphicsWidth = ReadLE16(demofile);
DemoGraphicsHeight = ReadFromStream<uint16_t>(demofile); DemoGraphicsHeight = ReadLE16(demofile);
while (!demofile.eof()) { while (!demofile.eof()) {
uint32_t typeNum = ReadFromStream<uint32_t>(demofile); const uint32_t typeNum = ReadLE32(demofile);
auto type = static_cast<DemoMsgType>(typeNum); const auto type = static_cast<DemoMsgType>(typeNum);
float progressToNextGameTick = ReadFromStream<float>(demofile); const float progressToNextGameTick = ReadLEFloat(demofile);
switch (type) { switch (type) {
case DemoMsgType::Message: { case DemoMsgType::Message: {
uint32_t message = ReadFromStream<uint32_t>(demofile); const uint32_t message = ReadLE32(demofile);
int32_t wParam = ReadFromStream<int32_t>(demofile); const uint32_t wParam = ReadLE32(demofile);
int32_t lParam = ReadFromStream<int32_t>(demofile); const uint32_t lParam = ReadLE32(demofile);
PumpDemoMessage(type, message, wParam, lParam, progressToNextGameTick); PumpDemoMessage(type, message, wParam, lParam, progressToNextGameTick);
break; break;
} }
@ -167,7 +147,7 @@ bool GetRunGameLoop(bool &drawGame, bool &processInput)
{ {
if (Demo_Message_Queue.empty()) if (Demo_Message_Queue.empty())
app_fatal("Demo 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) if (dmsg.type == DemoMsgType::Message)
app_fatal("Unexpected Message"); app_fatal("Unexpected Message");
if (Timedemo) { if (Timedemo) {
@ -230,7 +210,7 @@ bool FetchMessage(tagMSG *lpMsg)
} }
if (!Demo_Message_Queue.empty()) { if (!Demo_Message_Queue.empty()) {
demoMsg dmsg = Demo_Message_Queue.front(); const DemoMsg dmsg = Demo_Message_Queue.front();
if (dmsg.type == DemoMsgType::Message) { if (dmsg.type == DemoMsgType::Message) {
lpMsg->message = dmsg.message; lpMsg->message = dmsg.message;
lpMsg->lParam = dmsg.lParam; lpMsg->lParam = dmsg.lParam;
@ -250,30 +230,30 @@ bool FetchMessage(tagMSG *lpMsg)
void RecordGameLoopResult(bool runGameLoop) void RecordGameLoopResult(bool runGameLoop)
{ {
WriteToDemo<uint32_t>(static_cast<uint32_t>(runGameLoop ? DemoMsgType::GameTick : DemoMsgType::Rendering)); WriteLE32(DemoRecording, static_cast<uint32_t>(runGameLoop ? DemoMsgType::GameTick : DemoMsgType::Rendering));
WriteToDemo<float>(gfProgressToNextGameTick); WriteLEFloat(DemoRecording, gfProgressToNextGameTick);
} }
void RecordMessage(tagMSG *lpMsg) void RecordMessage(tagMSG *lpMsg)
{ {
if (!gbRunGame || !DemoRecording.is_open()) if (!gbRunGame || !DemoRecording.is_open())
return; return;
WriteToDemo<uint32_t>(static_cast<uint32_t>(DemoMsgType::Message)); WriteLE32(DemoRecording, static_cast<uint32_t>(DemoMsgType::Message));
WriteToDemo<float>(gfProgressToNextGameTick); WriteLEFloat(DemoRecording, gfProgressToNextGameTick);
WriteToDemo<uint32_t>(lpMsg->message); WriteLE32(DemoRecording, lpMsg->message);
WriteToDemo<uint32_t>(lpMsg->wParam); WriteLE32(DemoRecording, lpMsg->wParam);
WriteToDemo<uint32_t>(lpMsg->lParam); WriteLE32(DemoRecording, lpMsg->lParam);
} }
void NotifyGameLoopStart() void NotifyGameLoopStart()
{ {
if (IsRecording()) { if (IsRecording()) {
DemoRecording.open(StrCat(paths::PrefPath(), "demo_", RecordNumber, ".dmo"), std::fstream::trunc | std::fstream::binary); DemoRecording.open(StrCat(paths::PrefPath(), "demo_", RecordNumber, ".dmo"), std::fstream::trunc | std::fstream::binary);
constexpr uint8_t version = 0; constexpr uint8_t Version = 0;
WriteToDemo<uint8_t>(version); WriteByte(DemoRecording, Version);
WriteToDemo<uint32_t>(gSaveNumber); WriteLE32(DemoRecording, gSaveNumber);
WriteToDemo<uint16_t>(gnScreenWidth); WriteLE16(DemoRecording, gnScreenWidth);
WriteToDemo<uint16_t>(gnScreenHeight); WriteLE16(DemoRecording, gnScreenHeight);
} }
if (IsRunning()) { if (IsRunning()) {

27
Source/utils/endian.hpp

@ -1,31 +1,46 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <cstring>
#include <SDL_endian.h>
namespace devilution { namespace devilution {
template <typename T> template <typename T>
constexpr std::uint16_t LoadLE16(const T *b) constexpr uint16_t LoadLE16(const T *b)
{ {
static_assert(sizeof(T) == 1, "invalid argument"); static_assert(sizeof(T) == 1, "invalid argument");
// NOLINTNEXTLINE(readability-magic-numbers) // NOLINTNEXTLINE(readability-magic-numbers)
return (static_cast<std::uint16_t>(b[1]) << 8) | static_cast<std::uint16_t>(b[0]); return (static_cast<uint8_t>(b[1]) << 8) | static_cast<uint8_t>(b[0]);
} }
template <typename T> template <typename T>
constexpr std::uint32_t LoadLE32(const T *b) constexpr uint32_t LoadLE32(const T *b)
{ {
static_assert(sizeof(T) == 1, "invalid argument"); static_assert(sizeof(T) == 1, "invalid argument");
// NOLINTNEXTLINE(readability-magic-numbers) // NOLINTNEXTLINE(readability-magic-numbers)
return (static_cast<std::uint32_t>(b[3]) << 24) | (static_cast<std::uint32_t>(b[2]) << 16) | (static_cast<std::uint32_t>(b[1]) << 8) | static_cast<std::uint32_t>(b[0]); return (static_cast<uint8_t>(b[3]) << 24) | (static_cast<uint8_t>(b[2]) << 16) | (static_cast<uint8_t>(b[1]) << 8) | static_cast<uint8_t>(b[0]);
} }
template <typename T> template <typename T>
constexpr std::uint32_t LoadBE32(const T *b) constexpr uint32_t LoadBE32(const T *b)
{ {
static_assert(sizeof(T) == 1, "invalid argument"); static_assert(sizeof(T) == 1, "invalid argument");
// NOLINTNEXTLINE(readability-magic-numbers) // NOLINTNEXTLINE(readability-magic-numbers)
return (static_cast<std::uint32_t>(b[0]) << 24) | (static_cast<std::uint32_t>(b[1]) << 16) | (static_cast<std::uint32_t>(b[2]) << 8) | static_cast<std::uint32_t>(b[3]); return (static_cast<uint8_t>(b[0]) << 24) | (static_cast<uint8_t>(b[1]) << 16) | (static_cast<uint8_t>(b[2]) << 8) | static_cast<uint8_t>(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 } // namespace devilution

78
Source/utils/endian_stream.hpp

@ -0,0 +1,78 @@
#pragma once
#include <cstring>
#include <fstream>
#include <SDL_endian.h>
#include "utils/endian.hpp"
namespace devilution {
template <typename T = uint8_t>
T ReadByte(std::ifstream &stream)
{
static_assert(sizeof(T) == 1, "invalid argument");
char buf;
stream.read(&buf, 1);
return static_cast<T>(buf);
}
template <typename T = uint16_t>
T ReadLE16(std::ifstream &stream)
{
static_assert(sizeof(T) == 2, "invalid argument");
char buf[2];
stream.read(buf, 2);
return static_cast<T>(LoadLE16(buf));
}
template <typename T = uint32_t>
T ReadLE32(std::ifstream &stream)
{
static_assert(sizeof(T) == 4, "invalid argument");
char buf[4];
stream.read(buf, 4);
return static_cast<T>(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<const char *>(&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
Loading…
Cancel
Save