1

Half a year ago I decided to develop a sequel to my successful single-player game. My game was made in an existing game engine. This time I decided to create my own game engine with c++ 17 and directx 11 in visual studio 2019, because I have far too much free time nowadays and want to deal with the technical stuff that is otherwise hidden in existing game engines. I have almost implemented everything that I need. I need to store geometry data and so forth on disk in binary format so that my game can read these files when the game is shipped. The game also needs to be able to read/write binary save data. I am currently using text files for reading and writing... If I compile my win32 application on my intel cpu on windows 10 and ship my game on steam store, do my game need to be endian aware for binary files? My game will obviously require Windows and x86 CPU (Intel, AMD). I know that x86 CPU's use little-endian byte ordering.

Example code

static_assert(CHAR_BIT * sizeof(std::uint8_t) == 8);

std::wstring filename = L"MyBinaryFile.dat";
std::uint32_t dataOut = ...;

//My computer writes the data.
std::ofstream out(filename.c_str(), std::ios::out | std::ios::binary);
out.write(reinterpret_cast<char*>(&dataOut), sizeof(std::uint32_t));
out.close();

/**********************************************************************/

//Different computers running my game and reads the data.
std::uint32_t dataIn;

std::ifstream in(filename.c_str(), std::ios::in | std::ios::binary);
in.read(reinterpret_cast<char*>(&dataIn), sizeof(std::uint32_t));
in.close();

Is this safe for my game?

Or should I do it like this? is this safer?

static_assert(CHAR_BIT * sizeof(std::uint8_t) == 8);

std::wstring filename = L"MyBinaryFile.dat";
std::uint32_t dataOut = ...;

//My computer writes the data.
std::uint8_t bufferOut[4];
bufferOut[0] = static_cast<std::uint8_t>((dataOut & 0xff000000) >> 24);
bufferOut[1] = static_cast<std::uint8_t>((dataOut & 0x00ff0000) >> 16);
bufferOut[2] = static_cast<std::uint8_t>((dataOut & 0x0000ff00) >> 8);
bufferOut[3] = static_cast<std::uint8_t>((dataOut & 0x000000ff));

std::ofstream out(filename.c_str(), std::ios::out | std::ios::binary);
out.write(reinterpret_cast<char*>(bufferOut), 4 * sizeof(std::uint8_t));
out.close();

/**********************************************************************/

//Different computers running my game and reads the data.
std::uint8_t bufferIn[4];

std::ifstream in(filename.c_str(), std::ios::in | std::ios::binary);
in.read(reinterpret_cast<char*>(bufferIn), 4 * sizeof(std::uint8_t));
in.close();

std::uint32_t dataIn;
dataIn = static_cast<std::uint32_t>(bufferIn[0]) << 24;
dataIn |= static_cast<std::uint32_t>(bufferIn[1]) << 16;
dataIn |= static_cast<std::uint32_t>(bufferIn[2]) << 8;
dataIn |= static_cast<std::uint32_t>(bufferIn[3]);
GameDev
  • 11
  • 1
  • "I know that x86 CPU's use little-endian byte ordering" is pretty much the answer to your question. – molbdnilo Aug 23 '21 at 13:54
  • I am not sure how many modern CPU architectures are there (especially those for that have windows support) that use Big-endian. If you want to be safe you can just check endianness via `int x = 1;` and then checking `((char *)&x)[0]` to be `0` or one. Writing always in little endian and adjusting if necessary, but again if this is only for `x86`/`amd64` then this is a non issue – Lala5th Aug 23 '21 at 13:58
  • Pro: retrofitting endian-neutrality into existing code is a huge pain, if you suspect that at some point in the future you might need it, implement it now. Con: endianity of one machine will never suddenly change (re savegames), you would be shipping a different version on big-endian anyway so you can flip the data files on your end. –  Aug 23 '21 at 14:03
  • I'm going to use endian independent code. I want to avoid headaches in the future. Thanks! – GameDev Aug 24 '21 at 17:21

2 Answers2

0

Your second option is much safer and more portable.

In general, most modern CPUs use little endian representation, but there are some exceptions out there. In general, you should write your code in a platform-independent way whenever possible. Don't make assumptions about the hardware that someone will try to use to run your code. In the future, there might be some big endian Windows computers which can run other games on Steam, but not yours.

If you really want to speed up your program by using system-specific code, you should use preprocessor directives to do most of the heavy lifting. See: Determining endianness at compile time

Aaron Stanek
  • 408
  • 2
  • 9
0

There are htonl() and ntohl() for that (conversion host->network->host)

Vlad Feinstein
  • 10,960
  • 1
  • 12
  • 27