Browse Source

Use C FILE instead of C++ streams throughout

Reduces binary size by ~2 KiB and may improve compatibility with oddball
systems (e.g. PS2).
pull/5803/head
Gleb Mazovetskiy 3 years ago
parent
commit
1a1a282d9a
  1. 34
      Source/capture.cpp
  2. 65
      Source/debug.cpp
  3. 1
      Source/dvlnet/tcp_client.cpp
  4. 4
      Source/engine/assets.hpp
  5. 29
      Source/engine/demomode.cpp
  6. 32
      Source/mpq/mpq_writer.cpp
  7. 2
      Source/mpq/mpq_writer.hpp
  8. 22
      Source/options.cpp
  9. 83
      Source/pfile.cpp
  10. 33
      Source/utils/endian_stream.hpp
  11. 73
      Source/utils/file_util.cpp
  12. 4
      Source/utils/file_util.h
  13. 34
      Source/utils/logged_fstream.cpp
  14. 57
      Source/utils/logged_fstream.hpp
  15. 16
      test/file_util_test.cpp
  16. 14
      test/writehero_test.cpp

34
Source/capture.cpp

@ -4,8 +4,9 @@
* Implementation of the screenshot function.
*/
#include <cstdint>
#include <cstdio>
#include <fmt/chrono.h>
#include <fstream>
#include "DiabloUI/diabloui.h"
#include "engine/backbuffer_state.hpp"
@ -28,7 +29,7 @@ namespace {
* @param out File stream to write to
* @return True on success
*/
bool CaptureHdr(int16_t width, int16_t height, std::ofstream *out)
bool CaptureHdr(int16_t width, int16_t height, FILE *out)
{
PCXHeader buffer;
@ -44,8 +45,7 @@ bool CaptureHdr(int16_t width, int16_t height, std::ofstream *out)
buffer.NPlanes = 1;
buffer.BytesPerLine = SDL_SwapLE16(width);
out->write(reinterpret_cast<const char *>(&buffer), sizeof(buffer));
return !out->fail();
return std::fwrite(&buffer, sizeof(buffer), 1, out) == 1;
}
/**
@ -54,7 +54,7 @@ bool CaptureHdr(int16_t width, int16_t height, std::ofstream *out)
* @param out File stream for the PCX file.
* @return True if successful, else false
*/
bool CapturePal(SDL_Color *palette, std::ofstream *out)
bool CapturePal(SDL_Color *palette, FILE *out)
{
uint8_t pcxPalette[1 + 256 * 3];
@ -65,8 +65,7 @@ bool CapturePal(SDL_Color *palette, std::ofstream *out)
pcxPalette[1 + 3 * i + 2] = palette[i].b;
}
out->write(reinterpret_cast<const char *>(pcxPalette), sizeof(pcxPalette));
return !out->fail();
return std::fwrite(pcxPalette, sizeof(pcxPalette), 1, out) == 1;
}
/**
@ -118,7 +117,7 @@ uint8_t *CaptureEnc(uint8_t *src, uint8_t *dst, int width)
* @param out File stream for the PCX file.
* @return True if successful, else false
*/
bool CapturePix(const Surface &buf, std::ofstream *out)
bool CapturePix(const Surface &buf, FILE *out)
{
int width = buf.w();
std::unique_ptr<uint8_t[]> pBuffer { new uint8_t[2 * width] };
@ -126,14 +125,13 @@ bool CapturePix(const Surface &buf, std::ofstream *out)
for (int height = buf.h(); height > 0; height--) {
const uint8_t *pBufferEnd = CaptureEnc(pixels, pBuffer.get(), width);
pixels += buf.pitch();
out->write(reinterpret_cast<const char *>(pBuffer.get()), pBufferEnd - pBuffer.get());
if (out->fail())
if (std::fwrite(pBuffer.get(), pBufferEnd - pBuffer.get(), 1, out) != 1)
return false;
}
return true;
}
std::ofstream CaptureFile(std::string *dstPath)
FILE *CaptureFile(std::string *dstPath)
{
std::time_t tt = std::time(nullptr);
std::tm *tm = std::localtime(&tt);
@ -144,7 +142,7 @@ std::ofstream CaptureFile(std::string *dstPath)
i++;
*dstPath = StrCat(paths::PrefPath(), filename, "-", i, ".pcx");
}
return std::ofstream(*dstPath, std::ios::binary | std::ios::trunc);
return OpenFile(dstPath->c_str(), "wb");
}
/**
@ -168,22 +166,22 @@ void CaptureScreen()
std::string fileName;
bool success;
std::ofstream outStream = CaptureFile(&fileName);
if (!outStream.is_open())
FILE *outStream = CaptureFile(&fileName);
if (outStream == nullptr)
return;
DrawAndBlit();
PaletteGetEntries(256, palette);
RedPalette();
const Surface &buf = GlobalBackBuffer();
success = CaptureHdr(buf.w(), buf.h(), &outStream);
success = CaptureHdr(buf.w(), buf.h(), outStream);
if (success) {
success = CapturePix(buf, &outStream);
success = CapturePix(buf, outStream);
}
if (success) {
success = CapturePal(palette, &outStream);
success = CapturePal(palette, outStream);
}
outStream.close();
std::fclose(outStream);
if (!success) {
Log("Failed to save screenshot at {}", fileName);

65
Source/debug.cpp

@ -6,8 +6,7 @@
#ifdef _DEBUG
#include <fstream>
#include <sstream>
#include <cstdio>
#include "debug.h"
@ -29,9 +28,11 @@
#include "spells.h"
#include "towners.h"
#include "utils/endian_stream.hpp"
#include "utils/file_util.h"
#include "utils/language.h"
#include "utils/log.hpp"
#include "utils/str_cat.hpp"
#include "utils/str_split.hpp"
namespace devilution {
@ -262,21 +263,20 @@ std::string DebugCmdLoadMap(const string_view parameter)
int mapType = 0;
Point spawn = {};
std::stringstream paramsStream(parameter.data());
int count = 0;
for (std::string tmp; std::getline(paramsStream, tmp, ' ');) {
for (string_view arg : SplitByChar(parameter, ' ')) {
switch (count) {
case 0:
TestMapPath = StrCat(tmp, ".dun");
TestMapPath = StrCat(arg, ".dun");
break;
case 1:
mapType = atoi(tmp.c_str());
mapType = atoi(std::string(arg).c_str());
break;
case 2:
spawn.x = atoi(tmp.c_str());
spawn.x = atoi(std::string(arg).c_str());
break;
case 3:
spawn.y = atoi(tmp.c_str());
spawn.y = atoi(std::string(arg).c_str());
break;
}
count++;
@ -299,11 +299,9 @@ std::string DebugCmdLoadMap(const string_view parameter)
std::string ExportDun(const string_view parameter)
{
std::ofstream dunFile;
std::string levelName = StrCat(currlevel, "-", glSeedTbl[currlevel], ".dun");
dunFile.open(levelName, std::ios::out | std::ios::app | std::ios::binary);
FILE *dunFile = OpenFile(levelName.c_str(), "ab");
WriteLE16(dunFile, DMAXX);
WriteLE16(dunFile, DMAXY);
@ -361,7 +359,7 @@ std::string ExportDun(const string_view parameter)
WriteLE16(dunFile, dTransVal[x][y]);
}
}
dunFile.close();
std::fclose(dunFile);
return StrCat(levelName, " saved. Happy mapping!");
}
@ -424,18 +422,18 @@ std::string DebugCmdResetLevel(const string_view parameter)
{
Player &myPlayer = *MyPlayer;
std::stringstream paramsStream(parameter.data());
std::string singleParameter;
if (!std::getline(paramsStream, singleParameter, ' '))
auto args = SplitByChar(parameter, ' ');
auto it = args.begin();
if (it == args.end())
return "What level do you want to visit?";
auto level = atoi(singleParameter.c_str());
auto level = atoi(std::string(*it).c_str());
if (level < 0 || level > (gbIsHellfire ? 24 : 16))
return StrCat("Level ", level, " is not known. Do you want to write an extension mod?");
myPlayer._pLvlVisited[level] = false;
DeltaClearLevel(level);
if (std::getline(paramsStream, singleParameter, ' ')) {
uint32_t seed = static_cast<uint32_t>(std::stoul(singleParameter));
if (++it != args.end()) {
const auto seed = static_cast<uint32_t>(std::stoul(std::string(*it)));
glSeedTbl[level] = seed;
}
@ -682,15 +680,16 @@ std::string DebugCmdSpawnUniqueMonster(const string_view parameter)
if (leveltype == DTYPE_TOWN)
return "Do you want to kill the towners?!?";
std::stringstream paramsStream(parameter.data());
std::string name;
int count = 1;
for (std::string tmp; std::getline(paramsStream, tmp, ' '); name += tmp + " ") {
int num = atoi(tmp.c_str());
for (string_view arg : SplitByChar(parameter, ' ')) {
const int num = atoi(std::string(arg).c_str());
if (num > 0) {
count = num;
break;
}
AppendStrView(name, arg);
name += ' ';
}
if (name.empty())
return "Monster name cannot be empty. Duh.";
@ -769,15 +768,16 @@ std::string DebugCmdSpawnMonster(const string_view parameter)
if (leveltype == DTYPE_TOWN)
return "Do you want to kill the towners?!?";
std::stringstream paramsStream(parameter.data());
std::string name;
int count = 1;
for (std::string tmp; std::getline(paramsStream, tmp, ' '); name += tmp + " ") {
int num = atoi(tmp.c_str());
for (string_view arg : SplitByChar(parameter, ' ')) {
const int num = atoi(std::string(arg).c_str());
if (num > 0) {
count = num;
break;
}
AppendStrView(name, arg);
name += ' ';
}
if (name.empty())
return "Monster name cannot be empty. Duh.";
@ -979,21 +979,22 @@ std::string DebugCmdToggleFPS(const string_view parameter)
std::string DebugCmdChangeTRN(const string_view parameter)
{
std::stringstream paramsStream(parameter.data());
std::string first;
std::string out;
if (std::getline(paramsStream, first, ' ')) {
std::string second;
if (std::getline(paramsStream, second, ' ')) {
std::string prefix;
const auto parts = SplitByChar(parameter, ' ');
auto it = parts.begin();
if (it != parts.end()) {
const string_view first = *it;
if (++it != parts.end()) {
const string_view second = *it;
string_view prefix;
if (first == "mon") {
prefix = "monsters\\monsters\\";
} else if (first == "plr") {
prefix = "plrgfx\\";
}
debugTRN = prefix + second + ".trn";
debugTRN = StrCat(prefix, second, ".trn");
} else {
debugTRN = first + ".trn";
debugTRN = StrCat(first, ".trn");
}
out = fmt::format("I am a pretty butterfly. \n(Loading TRN: {:s})", debugTRN);
} else {

1
Source/dvlnet/tcp_client.cpp

@ -6,7 +6,6 @@
#include <exception>
#include <functional>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <system_error>

4
Source/engine/assets.hpp

@ -81,7 +81,7 @@ struct AssetHandle {
return std::fread(buffer, len, 1, handle) == 1;
}
bool seek(std::ios::pos_type pos)
bool seek(long pos)
{
return std::fseek(handle, pos, SEEK_SET) == 0;
}
@ -193,7 +193,7 @@ struct AssetHandle {
return handle->read(handle, buffer, len, 1) == 1;
}
bool seek(std::ios::pos_type pos)
bool seek(long pos)
{
return handle->seek(handle, pos, RW_SEEK_SET) != -1;
}

29
Source/engine/demomode.cpp

@ -1,9 +1,7 @@
#include "engine/demomode.h"
#include <cstdio>
#include <deque>
#include <fstream>
#include <iostream>
#include <sstream>
#ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
@ -76,7 +74,7 @@ bool Timedemo = false;
int RecordNumber = -1;
bool CreateDemoReference = false;
std::ofstream DemoRecording;
FILE *DemoRecording;
std::deque<DemoMsg> Demo_Message_Queue;
uint32_t DemoModeLastTick = 0;
@ -277,9 +275,9 @@ void LogDemoMessage(const DemoMsg &msg)
bool LoadDemoMessages(int i)
{
std::ifstream demofile;
demofile.open(StrCat(paths::PrefPath(), "demo_", i, ".dmo"), std::fstream::binary);
if (!demofile.is_open()) {
const std::string path = StrCat(paths::PrefPath(), "demo_", i, ".dmo");
FILE *demofile = OpenFile(path.c_str(), "rb");
if (demofile == nullptr) {
return false;
}
@ -294,7 +292,7 @@ bool LoadDemoMessages(int i)
while (true) {
const uint32_t typeNum = ReadLE32(demofile);
if (demofile.eof())
if (std::feof(demofile))
break;
const auto type = static_cast<DemoMsgType>(typeNum);
@ -343,7 +341,7 @@ bool LoadDemoMessages(int i)
}
}
demofile.close();
std::fclose(demofile);
DemoModeLastTick = SDL_GetTicks();
@ -496,7 +494,7 @@ void RecordGameLoopResult(bool runGameLoop)
void RecordMessage(const SDL_Event &event, uint16_t modState)
{
if (!gbRunGame || !DemoRecording.is_open())
if (!gbRunGame || DemoRecording == nullptr)
return;
if (CurrentEventHandler == DisableInputEventHandler)
return;
@ -553,7 +551,13 @@ void RecordMessage(const SDL_Event &event, uint16_t modState)
void NotifyGameLoopStart()
{
if (IsRecording()) {
DemoRecording.open(StrCat(paths::PrefPath(), "demo_", RecordNumber, ".dmo"), std::fstream::trunc | std::fstream::binary);
const std::string path = StrCat(paths::PrefPath(), "demo_", RecordNumber, ".dmo");
DemoRecording = OpenFile(path.c_str(), "wb");
if (DemoRecording == nullptr) {
RecordNumber = -1;
LogError("Failed to open {} for writing", path);
return;
}
constexpr uint8_t Version = 0;
WriteByte(DemoRecording, Version);
WriteLE32(DemoRecording, gSaveNumber);
@ -570,7 +574,8 @@ void NotifyGameLoopStart()
void NotifyGameLoopEnd()
{
if (IsRecording()) {
DemoRecording.close();
std::fclose(DemoRecording);
DemoRecording = nullptr;
if (CreateDemoReference)
pfile_write_hero_demo(RecordNumber);

32
Source/mpq/mpq_writer.cpp

@ -48,8 +48,8 @@ constexpr uint32_t HashEntrySize = BlockEntriesCount * sizeof(MpqHashEntry);
// We store the block and the hash entry tables immediately after the header.
// This is unlike most other MPQ archives, that store these at the end of the file.
constexpr std::ios::off_type MpqBlockEntryOffset = sizeof(MpqFileHeader);
constexpr std::ios::off_type MpqHashEntryOffset = MpqBlockEntryOffset + BlockEntrySize;
constexpr long MpqBlockEntryOffset = sizeof(MpqFileHeader);
constexpr long MpqHashEntryOffset = MpqBlockEntryOffset + BlockEntrySize;
// Special return value for `GetHashIndex` and `GetHandle`.
constexpr uint32_t HashEntryNotFound = -1;
@ -100,21 +100,19 @@ MpqWriter::MpqWriter(const char *path)
LogVerbose("Opening {}", path);
std::string error;
bool exists = FileExists(path);
std::ios::openmode mode = std::ios::out | std::ios::binary;
const char *mode = "wb";
if (exists) {
mode |= std::ios::in;
mode = "r+b";
if (!GetFileSize(path, &size_)) {
error = R"(GetFileSize failed: "{}")";
LogError(error, path, std::strerror(errno));
goto on_error;
}
LogVerbose("GetFileSize(\"{}\") = {}", path, size_);
} else {
mode |= std::ios::trunc;
}
if (!stream_.Open(path, mode)) {
stream_.Close();
error = "Failed to open fstream";
error = "Failed to open file";
goto on_error;
}
@ -131,7 +129,7 @@ MpqWriter::MpqWriter(const char *path)
blockTable_ = std::make_unique<MpqBlockEntry[]>(BlockEntriesCount);
std::memset(blockTable_.get(), 0, BlockEntriesCount * sizeof(MpqBlockEntry));
if (fhdr.blockEntriesCount > 0) {
if (!stream_.Read(reinterpret_cast<char *>(blockTable_.get()), static_cast<std::streamsize>(fhdr.blockEntriesCount * sizeof(MpqBlockEntry)))) {
if (!stream_.Read(reinterpret_cast<char *>(blockTable_.get()), static_cast<size_t>(fhdr.blockEntriesCount * sizeof(MpqBlockEntry)))) {
error = "Failed to read block table";
goto on_error;
}
@ -144,7 +142,7 @@ MpqWriter::MpqWriter(const char *path)
std::memset(hashTable_.get(), 0xFF, HashEntriesCount * sizeof(MpqHashEntry));
if (fhdr.hashEntriesCount > 0) {
if (!stream_.Read(reinterpret_cast<char *>(hashTable_.get()), static_cast<std::streamsize>(fhdr.hashEntriesCount * sizeof(MpqHashEntry)))) {
if (!stream_.Read(reinterpret_cast<char *>(hashTable_.get()), static_cast<size_t>(fhdr.hashEntriesCount * sizeof(MpqHashEntry)))) {
error = "Failed to read hash entries";
goto on_error;
}
@ -153,7 +151,7 @@ MpqWriter::MpqWriter(const char *path)
}
#ifndef CAN_SEEKP_BEYOND_EOF
if (!stream_.Seekp(0, std::ios::beg))
if (!stream_.Seekp(0, SEEK_SET))
goto on_error;
// Memorize stream begin, we'll need it for calculations later.
@ -178,7 +176,7 @@ MpqWriter::~MpqWriter()
LogVerbose("Closing {}", name_);
bool result = true;
if (!(stream_.Seekp(0, std::ios::beg) && WriteHeaderAndTables()))
if (!(stream_.Seekp(0, SEEK_SET) && WriteHeaderAndTables()))
result = false;
stream_.Close();
if (result && size_ != 0) {
@ -395,12 +393,12 @@ bool MpqWriter::WriteFileContents(const char *filename, const byte *fileData, si
std::unique_ptr<uint32_t[]> offsetTable { new uint32_t[numSectors + 1] };
#ifdef CAN_SEEKP_BEYOND_EOF
if (!stream_.Seekp(block->offset + offsetTableByteSize, std::ios::beg))
if (!stream_.Seekp(block->offset + offsetTableByteSize, SEEK_SET))
return false;
#else
// Ensure we do not Seekp beyond EOF by filling the missing space.
std::streampos stream_end;
if (!stream_.Seekp(0, std::ios::end) || !stream_.Tellp(&stream_end))
long stream_end;
if (!stream_.Seekp(0, SEEK_END) || !stream_.Tellp(&stream_end))
return false;
const std::uintmax_t cur_size = stream_end - streamBegin_;
if (cur_size < block->offset + offsetTableByteSize) {
@ -412,7 +410,7 @@ bool MpqWriter::WriteFileContents(const char *filename, const byte *fileData, si
if (!stream_.Write(reinterpret_cast<const char *>(offsetTable.get()), offsetTableByteSize))
return false;
} else {
if (!stream_.Seekp(block->offset + offsetTableByteSize, std::ios::beg))
if (!stream_.Seekp(block->offset + offsetTableByteSize, SEEK_SET))
return false;
}
#endif
@ -436,11 +434,11 @@ bool MpqWriter::WriteFileContents(const char *filename, const byte *fileData, si
}
offsetTable[numSectors] = SDL_SwapLE32(destSize);
if (!stream_.Seekp(block->offset, std::ios::beg))
if (!stream_.Seekp(block->offset, SEEK_SET))
return false;
if (!stream_.Write(reinterpret_cast<const char *>(offsetTable.get()), offsetTableByteSize))
return false;
if (!stream_.Seekp(destSize - offsetTableByteSize, std::ios::cur))
if (!stream_.Seekp(destSize - offsetTableByteSize, SEEK_CUR))
return false;
if (destSize < block->packedSize) {

2
Source/mpq/mpq_writer.hpp

@ -67,7 +67,7 @@ private:
#endif
#ifndef CAN_SEEKP_BEYOND_EOF
std::streampos streamBegin_;
long streamBegin_;
#endif
};

22
Source/options.cpp

@ -5,11 +5,10 @@
*/
#include <cstdint>
#include <fstream>
#include <cstdio>
#include <fmt/format.h>
#define SI_SUPPORT_IOSTREAMS
#define SI_NO_CONVERSION
#include <SimpleIni.h>
@ -92,11 +91,13 @@ CSimpleIni &GetIni()
static bool isIniLoaded = false;
if (!isIniLoaded) {
auto path = GetIniPath();
auto stream = CreateFileStream(path.c_str(), std::fstream::in | std::fstream::binary);
FILE *file = OpenFile(path.c_str(), "rb");
ini.SetSpaces(false);
ini.SetMultiKey();
if (stream)
ini.LoadData(*stream);
if (file != nullptr) {
ini.LoadFile(file);
std::fclose(file);
}
isIniLoaded = true;
}
return ini;
@ -244,9 +245,14 @@ void SaveIni()
}
}
#endif
auto iniPath = GetIniPath();
auto stream = CreateFileStream(iniPath.c_str(), std::fstream::out | std::fstream::trunc | std::fstream::binary);
GetIni().Save(*stream, true);
const std::string iniPath = GetIniPath();
FILE *file = OpenFile(iniPath.c_str(), "wb");
if (file != nullptr) {
GetIni().SaveFile(file, true);
std::fclose(file);
} else {
LogError("Failed to write ini file to {}: {}", iniPath, std::strerror(errno));
}
IniChanged = false;
}

83
Source/pfile.cpp

@ -5,7 +5,6 @@
*/
#include "pfile.h"
#include <sstream>
#include <string>
#include <unordered_map>
@ -28,6 +27,7 @@
#include "utils/stdcompat/abs.hpp"
#include "utils/stdcompat/string_view.hpp"
#include "utils/str_cat.hpp"
#include "utils/str_split.hpp"
#include "utils/utf8.hpp"
#ifdef UNPACKED_SAVES
@ -162,14 +162,8 @@ SaveWriter GetStashWriter()
#ifndef DISABLE_DEMOMODE
void CopySaveFile(uint32_t saveNum, std::string targetPath)
{
std::string savePath = GetSavePath(saveNum);
auto saveStream = CreateFileStream(savePath.c_str(), std::fstream::in | std::fstream::binary);
if (!saveStream)
return;
auto targetStream = CreateFileStream(targetPath.c_str(), std::fstream::out | std::fstream::binary | std::fstream::trunc);
if (!targetStream)
return;
*targetStream << saveStream->rdbuf();
const std::string savePath = GetSavePath(saveNum);
CopyFileOverwrite(savePath.c_str(), targetPath.c_str());
}
#endif
@ -236,15 +230,6 @@ std::optional<SaveReader> CreateSaveReader(std::string &&path)
}
#ifndef DISABLE_DEMOMODE
class MemoryBuffer : public std::basic_streambuf<char> {
public:
MemoryBuffer(char *data, size_t byteCount)
{
setg(data, data, data + byteCount);
setp(data, data + byteCount);
}
};
struct CompareInfo {
std::unique_ptr<byte[]> &data;
size_t currentPosition;
@ -269,11 +254,11 @@ struct CompareCounter {
}
};
inline bool string_ends_with(std::string const &value, std::string const &ending)
inline bool string_ends_with(string_view value, string_view suffix)
{
if (ending.size() > value.size())
if (suffix.size() > value.size())
return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
return std::equal(suffix.rbegin(), suffix.rend(), value.rbegin());
}
void CreateDetailDiffs(string_view prefix, string_view memoryMapFile, CompareInfo &compareInfoReference, CompareInfo &compareInfoActual, std::unordered_map<std::string, size_t> &foundDiffs)
@ -290,9 +275,7 @@ void CreateDetailDiffs(string_view prefix, string_view memoryMapFile, CompareInf
size_t readBytes = SDL_RWsize(handle);
std::unique_ptr<byte[]> memoryMapFileData { new byte[readBytes] };
SDL_RWread(handle, memoryMapFileData.get(), readBytes, 1);
MemoryBuffer buffer(reinterpret_cast<char *>(memoryMapFileData.get()), readBytes);
std::istream reader(&buffer);
const string_view buffer(reinterpret_cast<const char *>(memoryMapFileData.get()), readBytes);
std::unordered_map<std::string, CompareCounter> counter;
@ -341,15 +324,18 @@ void CreateDetailDiffs(string_view prefix, string_view memoryMapFile, CompareInf
return value;
};
std::string line;
while (std::getline(reader, line)) {
if (line.size() > 0 && line[line.size() - 1] == '\r')
line.resize(line.size() - 1);
if (line.size() == 0)
for (string_view line : SplitByChar(buffer, '\n')) {
if (!line.empty() && line.back() == '\r')
line.remove_suffix(1);
if (line.empty())
continue;
const auto tokens = SplitByChar(line, ' ');
auto it = tokens.begin();
const auto end = tokens.end();
if (it == end)
continue;
std::stringstream lineStream(line);
std::string command;
std::getline(lineStream, command, ' ');
string_view command = *it;
bool dataExistsReference = compareInfoReference.dataExists;
bool dataExistsActual = compareInfoActual.dataExists;
@ -357,12 +343,12 @@ void CreateDetailDiffs(string_view prefix, string_view memoryMapFile, CompareInf
if (string_ends_with(command, "_HF")) {
if (!gbIsHellfire)
continue;
command.resize(command.size() - 3);
command.remove_suffix(3);
}
if (string_ends_with(command, "_DA")) {
if (gbIsHellfire)
continue;
command.resize(command.size() - 3);
command.remove_suffix(3);
}
if (string_ends_with(command, "_DL")) {
if (compareInfoReference.isTownLevel && compareInfoActual.isTownLevel)
@ -371,14 +357,11 @@ void CreateDetailDiffs(string_view prefix, string_view memoryMapFile, CompareInf
compareInfoReference.dataExists = false;
if (compareInfoActual.isTownLevel)
compareInfoActual.dataExists = false;
command.resize(command.size() - 3);
command.remove_suffix(3);
}
if (command == "R" || command == "LT" || command == "LC" || command == "LC_LE") {
std::string bitsAsString;
std::getline(lineStream, bitsAsString, ' ');
std::string comment;
std::getline(lineStream, comment);
const auto bitsAsString = std::string(*++it);
const auto comment = std::string(*++it);
size_t bytes = static_cast<size_t>(std::stoi(bitsAsString) / 8);
if (command == "LT") {
@ -392,7 +375,7 @@ void CreateDetailDiffs(string_view prefix, string_view memoryMapFile, CompareInf
int32_t valueReference = read32BitInt(compareInfoReference, command == "LC_LE");
int32_t valueActual = read32BitInt(compareInfoActual, command == "LC_LE");
assert(sizeof(valueReference) == bytes);
counter.insert_or_assign(comment, CompareCounter { valueReference, valueActual });
counter.insert_or_assign(std::string(comment), CompareCounter { valueReference, valueActual });
}
if (!compareBytes(bytes)) {
@ -400,12 +383,9 @@ void CreateDetailDiffs(string_view prefix, string_view memoryMapFile, CompareInf
addDiff(diffKey);
}
} else if (command == "M") {
std::string countAsString;
std::getline(lineStream, countAsString, ' ');
std::string bitsAsString;
std::getline(lineStream, bitsAsString, ' ');
std::string comment;
std::getline(lineStream, comment);
const auto countAsString = std::string(*++it);
const auto bitsAsString = std::string(*++it);
string_view comment = *++it;
CompareCounter count = getCounter(countAsString);
size_t bytes = static_cast<size_t>(std::stoi(bitsAsString) / 8);
@ -417,12 +397,9 @@ void CreateDetailDiffs(string_view prefix, string_view memoryMapFile, CompareInf
}
}
} else if (command == "C") {
std::string countAsString;
std::getline(lineStream, countAsString, ' ');
std::string subMemoryMapFile;
std::getline(lineStream, subMemoryMapFile, ' ');
std::string comment;
std::getline(lineStream, comment);
const auto countAsString = std::string(*++it);
auto subMemoryMapFile = std::string(*++it);
const auto comment = std::string(*++it);
CompareCounter count = getCounter(countAsString);
subMemoryMapFile.erase(std::remove(subMemoryMapFile.begin(), subMemoryMapFile.end(), '\r'), subMemoryMapFile.end());

33
Source/utils/endian_stream.hpp

@ -1,41 +1,40 @@
#pragma once
#include <cstdio>
#include <cstring>
#include <fstream>
#include "utils/endian.hpp"
namespace devilution {
template <typename T = uint8_t>
T ReadByte(std::ifstream &stream)
T ReadByte(FILE *stream)
{
static_assert(sizeof(T) == 1, "invalid argument");
char buf;
stream.read(&buf, 1);
std::fread(&buf, sizeof(buf), 1, stream);
return static_cast<T>(buf);
}
template <typename T = uint16_t>
T ReadLE16(std::ifstream &stream)
T ReadLE16(FILE *stream)
{
static_assert(sizeof(T) == 2, "invalid argument");
char buf[2];
stream.read(buf, 2);
std::fread(buf, sizeof(buf), 1, stream);
return static_cast<T>(LoadLE16(buf));
}
template <typename T = uint32_t>
T ReadLE32(std::ifstream &stream)
T ReadLE32(FILE *stream)
{
static_assert(sizeof(T) == 4, "invalid argument");
char buf[4];
stream.read(buf, 4);
std::fread(buf, sizeof(buf), 1, stream);
return static_cast<T>(LoadLE32(buf));
}
inline float ReadLEFloat(std::ifstream &stream)
inline float ReadLEFloat(FILE *stream)
{
static_assert(sizeof(float) == sizeof(uint32_t), "invalid float size");
const uint32_t val = ReadLE32(stream);
@ -44,33 +43,33 @@ inline float ReadLEFloat(std::ifstream &stream)
return result;
}
inline void WriteByte(std::ofstream &out, uint8_t val)
inline void WriteByte(FILE *out, uint8_t val)
{
out.write(reinterpret_cast<const char *>(&val), 1);
std::fwrite(&val, sizeof(val), 1, out);
}
inline void WriteLE16(std::ofstream &out, uint16_t val)
inline void WriteLE16(FILE *out, uint16_t val)
{
char data[2];
WriteLE16(data, val);
out.write(data, 2);
std::fwrite(data, sizeof(data), 1, out);
}
inline void WriteLE32(std::ofstream &out, uint32_t val)
inline void WriteLE32(FILE *out, uint32_t val)
{
char data[4];
WriteLE32(data, val);
out.write(data, 4);
std::fwrite(data, sizeof(data), 1, out);
}
inline void WriteLEFloat(std::ofstream &out, float val)
inline void WriteLEFloat(FILE *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);
std::fwrite(data, sizeof(data), 1, out);
}
} // namespace devilution

73
Source/utils/file_util.cpp

@ -1,5 +1,8 @@
#include "utils/file_util.h"
#include <cerrno>
#include <cstring>
#include <algorithm>
#include <string>
@ -7,6 +10,7 @@
#include "utils/log.hpp"
#include "utils/stdcompat/filesystem.hpp"
#include "utils/stdcompat/string_view.hpp"
#ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
@ -30,6 +34,10 @@
#include <unistd.h>
#endif
#ifdef __APPLE__
#include <copyfile.h>
#endif
namespace devilution {
#if (defined(_WIN64) || defined(_WIN32)) && !defined(NXDK)
@ -203,12 +211,61 @@ void RenameFile(const char *from, const char *to)
::MoveFileW(&fromUtf16[0], &toUtf16[0]);
#elif defined(DVL_HAS_FILESYSTEM)
std::error_code ec;
return std::filesystem::rename(from, to);
std::filesystem::rename(from, to, ec);
#else
::rename(from, to);
#endif
}
void CopyFileOverwrite(const char *from, const char *to)
{
#if defined(NXDK)
if (!::CopyFile(from, to, /*bFailIfExists=*/false)) {
LogError("Failed to copy {} to {}", from, to);
}
#elif defined(_WIN64) || defined(_WIN32)
const auto fromUtf16 = ToWideChar(from);
const auto toUtf16 = ToWideChar(to);
if (fromUtf16 == nullptr || toUtf16 == nullptr) {
LogError("UTF-8 -> UTF-16 conversion error code {}", ::GetLastError());
return;
}
if (!::CopyFileW(&fromUtf16[0], &toUtf16[0], /*bFailIfExists=*/false)) {
LogError("Failed to copy {} to {}", from, to);
}
#elif defined(__APPLE__)
::copyfile(from, to, nullptr, COPYFILE_ALL);
#elif defined(DVL_HAS_FILESYSTEM)
std::error_code error;
std::filesystem::copy_file(from, to, std::filesystem::copy_options::overwrite_existing, error);
if (error) {
LogError("Failed to copy {} to {}: {}", from, to, error.message());
}
#else
FILE *infile = OpenFile(from, "rb");
if (infile == nullptr) {
LogError("Failed to open {} for reading: {}", from, std::strerror(errno));
return;
}
FILE *outfile = OpenFile(to, "wb");
if (outfile == nullptr) {
LogError("Failed to open {} for writing: {}", to, std::strerror(errno));
std::fclose(infile);
return;
}
char buffer[4096];
size_t numRead;
while ((numRead = std::fread(buffer, sizeof(char), sizeof(buffer), infile)) > 0) {
if (std::fwrite(buffer, sizeof(char), numRead, outfile) != numRead) {
LogError("Write failed {}: {}", to, std::strerror(errno));
break;
}
}
std::fclose(infile);
std::fclose(outfile);
#endif
}
void RemoveFile(const char *path)
{
#if defined(NXDK)
@ -235,20 +292,6 @@ void RemoveFile(const char *path)
#endif
}
std::optional<std::fstream> CreateFileStream(const char *path, std::ios::openmode mode)
{
#if (defined(_WIN64) || defined(_WIN32)) && !defined(NXDK)
const auto pathUtf16 = ToWideChar(path);
if (pathUtf16 == nullptr) {
LogError("UTF-8 -> UTF-16 conversion error code {}", ::GetLastError());
return {};
}
return { std::fstream(pathUtf16.get(), mode) };
#else
return { std::fstream(path, mode) };
#endif
}
FILE *OpenFile(const char *path, const char *mode)
{
#if (defined(_WIN64) || defined(_WIN32)) && !defined(NXDK)

4
Source/utils/file_util.h

@ -2,11 +2,9 @@
#include <cstdint>
#include <cstdio>
#include <fstream>
#include <memory>
#include <string>
#include "utils/stdcompat/optional.hpp"
#include "utils/stdcompat/string_view.hpp"
namespace devilution {
@ -22,8 +20,8 @@ bool FileExistsAndIsWriteable(const char *path);
bool GetFileSize(const char *path, std::uintmax_t *size);
bool ResizeFile(const char *path, std::uintmax_t size);
void RenameFile(const char *from, const char *to);
void CopyFileOverwrite(const char *from, const char *to);
void RemoveFile(const char *path);
std::optional<std::fstream> CreateFileStream(const char *path, std::ios::openmode mode);
FILE *OpenFile(const char *path, const char *mode);
#if (defined(_WIN64) || defined(_WIN32)) && !defined(NXDK)

34
Source/utils/logged_fstream.cpp

@ -2,38 +2,18 @@
namespace devilution {
const char *LoggedFStream::DirToString(std::ios::seekdir dir)
const char *LoggedFStream::DirToString(int dir)
{
switch (dir) {
case std::ios::beg:
return "std::ios::beg";
case std::ios::end:
return "std::ios::end";
case std::ios::cur:
return "std::ios::cur";
case SEEK_SET:
return "SEEK_SET";
case SEEK_END:
return "SEEK_END";
case SEEK_CUR:
return "SEEK_CUR";
default:
return "invalid";
}
}
std::string LoggedFStream::OpenModeToString(std::ios::openmode mode)
{
std::string result;
if ((mode & std::ios::app) != 0)
result.append("std::ios::app | ");
if ((mode & std::ios::ate) != 0)
result.append("std::ios::ate | ");
if ((mode & std::ios::binary) != 0)
result.append("std::ios::binary | ");
if ((mode & std::ios::in) != 0)
result.append("std::ios::in | ");
if ((mode & std::ios::out) != 0)
result.append("std::ios::out | ");
if ((mode & std::ios::trunc) != 0)
result.append("std::ios::trunc | ");
if (!result.empty())
result.resize(result.size() - 3);
return result;
}
} // namespace devilution

57
Source/utils/logged_fstream.hpp

@ -1,8 +1,8 @@
#pragma once
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <string>
#include "utils/file_util.h"
@ -11,63 +11,60 @@
namespace devilution {
// A wrapper around `std::fstream` that logs errors.
// A wrapper around `FILE *` that logs errors.
struct LoggedFStream {
public:
bool Open(const char *path, std::ios::openmode mode)
bool Open(const char *path, const char *mode)
{
s_ = CreateFileStream(path, mode);
return CheckError("new std::fstream(\"{}\", {})", path, OpenModeToString(mode).c_str());
s_ = OpenFile(path, mode);
return CheckError("fopen(\"{}\", \"{}\")", path, mode);
}
void Close()
{
s_ = std::nullopt;
if (s_ != nullptr) {
std::fclose(s_);
s_ = nullptr;
}
}
[[nodiscard]] bool IsOpen() const
{
return s_ != std::nullopt;
}
bool Seekp(std::streampos pos)
{
s_->seekp(pos);
return CheckError("seekp({})", pos);
return s_ != nullptr;
}
bool Seekp(std::streamoff pos, std::ios::seekdir dir)
bool Seekp(long pos, int dir = SEEK_SET)
{
s_->seekp(pos, dir);
return CheckError("seekp({}, {})", pos, DirToString(dir));
std::fseek(s_, pos, dir);
return CheckError("fseek({}, {})", pos, DirToString(dir));
}
bool Tellp(std::streampos *result)
bool Tellp(long *result)
{
*result = s_->tellp();
return CheckError("tellp() = {}", *result);
*result = std::ftell(s_);
return CheckError("ftell() = {}", *result);
}
bool Write(const char *data, std::streamsize size)
bool Write(const char *data, size_t size)
{
s_->write(data, size);
return CheckError("write(data, {})", size);
std::fwrite(data, size, 1, s_);
return CheckError("fwrite(data, {})", size);
}
bool Read(char *out, std::streamsize size)
bool Read(char *out, size_t size)
{
s_->read(out, size);
return CheckError("read(out, {})", size);
std::fread(out, size, 1, s_);
return CheckError("fread(out, {})", size);
}
private:
static const char *DirToString(std::ios::seekdir dir);
static std::string OpenModeToString(std::ios::openmode mode);
static const char *DirToString(int dir);
template <typename... PrintFArgs>
bool CheckError(const char *fmt, PrintFArgs... args)
{
if (s_->fail()) {
const bool ok = s_ != nullptr && std::ferror(s_) == 0;
if (!ok) {
std::string fmtWithError = fmt;
fmtWithError.append(": failed with \"{}\"");
const char *errorMessage = std::strerror(errno);
@ -77,10 +74,10 @@ private:
} else {
LogVerbose(LogCategory::System, fmt, args...);
}
return !s_->fail();
return ok;
}
std::optional<std::fstream> s_;
FILE *s_ = nullptr;
};
} // namespace devilution

16
test/file_util_test.cpp

@ -1,7 +1,6 @@
#include <gtest/gtest.h>
#include <fstream>
#include <iostream>
#include <cstdio>
#include "utils/file_util.h"
@ -11,13 +10,15 @@ namespace {
void WriteDummyFile(const char *name, std::uintmax_t size)
{
std::ofstream test_file(name, std::ios::out | std::ios::trunc | std::ios::binary);
ASSERT_FALSE(test_file.fail());
std::printf("Writing test file %s\n", name);
FILE *test_file = std::fopen(name, "wb");
ASSERT_TRUE(test_file != nullptr);
const char c = '\0';
for (std::uintmax_t i = 0; i < size; ++i) {
test_file.write(&c, 1);
ASSERT_FALSE(test_file.fail());
std::fwrite(&c, sizeof(c), 1, test_file);
ASSERT_EQ(std::ferror(test_file), 0);
}
std::fclose(test_file);
}
std::string GetTmpPathName(const char *suffix = ".tmp")
@ -34,7 +35,6 @@ std::string GetTmpPathName(const char *suffix = ".tmp")
TEST(FileUtil, GetFileSize)
{
const std::string path = GetTmpPathName();
std::cout << path << std::endl;
WriteDummyFile(path.c_str(), 42);
std::uintmax_t result;
ASSERT_TRUE(GetFileSize(path.c_str(), &result));
@ -45,7 +45,6 @@ TEST(FileUtil, FileExists)
{
EXPECT_FALSE(FileExists("this-file-should-not-exist"));
const std::string path = GetTmpPathName();
std::cout << path << std::endl;
WriteDummyFile(path.c_str(), 42);
EXPECT_TRUE(FileExists(path.c_str()));
}
@ -53,7 +52,6 @@ TEST(FileUtil, FileExists)
TEST(FileUtil, ResizeFile)
{
const std::string path = GetTmpPathName();
std::cout << path << std::endl;
WriteDummyFile(path.c_str(), 42);
std::uintmax_t size;
ASSERT_TRUE(GetFileSize(path.c_str(), &size));

14
test/writehero_test.cpp

@ -2,7 +2,6 @@
#include <cstdint>
#include <cstdio>
#include <fstream>
#include <vector>
#include <SDL_endian.h>
@ -12,6 +11,7 @@
#include "loadsave.h"
#include "pack.h"
#include "pfile.h"
#include "utils/file_util.h"
#include "utils/paths.h"
namespace devilution {
@ -389,9 +389,17 @@ TEST(Writehero, pfile_write_hero)
AssertPlayer(Players[0]);
pfile_write_hero();
std::ifstream f("multi_0.sv", std::ios::binary);
const char *path = "multi_0.sv";
uintmax_t size;
ASSERT_TRUE(GetFileSize(path, &size));
FILE *f = std::fopen(path, "rb");
ASSERT_TRUE(f != nullptr);
std::unique_ptr<char[]> data { new char[size] };
ASSERT_EQ(std::fread(data.get(), size, 1, f), 1);
std::fclose(f);
std::vector<unsigned char> s(picosha2::k_digest_size);
picosha2::hash256(f, s.begin(), s.end());
picosha2::hash256(data.get(), data.get() + size, s.begin(), s.end());
EXPECT_EQ(picosha2::bytes_to_hex_string(s.begin(), s.end()),
"a79367caae6192d54703168d82e0316aa289b2a33251255fad8abe34889c1d3a");
}

Loading…
Cancel
Save