Browse Source

`UNPACKED_SAVES`: Saves without MPQs

Reduces rg99 binary size by ~70 KiB.
pull/5690/head
Gleb Mazovetskiy 3 years ago
parent
commit
6d274c9547
  1. 1
      CMake/Definitions.cmake
  2. 1
      CMake/platforms/rg99.cmake
  3. 1
      CMakeLists.txt
  4. 14
      Source/CMakeLists.txt
  5. 5
      Source/engine/assets.cpp
  6. 5
      Source/init.cpp
  7. 6
      Source/init.h
  8. 26
      Source/loadsave.cpp
  9. 14
      Source/loadsave.h
  10. 4
      Source/mpq/mpq_writer.hpp
  11. 142
      Source/pfile.cpp
  12. 70
      Source/pfile.h
  13. 28
      Source/utils/file_util.cpp
  14. 1
      Source/utils/file_util.h
  15. 6
      Source/utils/paths.cpp
  16. 2
      Source/utils/paths.h

1
CMake/Definitions.cmake

@ -20,6 +20,7 @@ foreach(
DEVILUTIONX_RESAMPLER_SDL
DEVILUTIONX_PALETTE_TRANSPARENCY_BLACK_16_LUT
UNPACKED_MPQS
UNPACKED_SAVES
)
if(${def_name})
list(APPEND DEVILUTIONX_DEFINITIONS ${def_name})

1
CMake/platforms/rg99.cmake

@ -1,6 +1,7 @@
# RG99 has the same layout as RG300 but only 32 MiB RAM
set(BUILD_ASSETS_MPQ OFF)
set(UNPACKED_MPQS ON)
set(UNPACKED_SAVES ON)
set(NONET ON)
set(USE_SDL1 ON)

1
CMakeLists.txt

@ -125,6 +125,7 @@ include(MoldLinker)
# Memory / performance trade-off options
option(UNPACKED_MPQS "Expect MPQs to be unpacked and the data converted with devilutionx-mpq-tools" OFF)
option(UNPACKED_SAVES "Uses unpacked save files instead of MPQ .sv/.hsv files" OFF)
option(DISABLE_STREAMING_MUSIC "Disable streaming music (to work around broken platform implementations)" OFF)
mark_as_advanced(DISABLE_STREAMING_MUSIC)
option(DISABLE_STREAMING_SOUNDS "Disable streaming sounds (to work around broken platform implementations)" OFF)

14
Source/CMakeLists.txt

@ -129,10 +129,6 @@ set(libdevilutionx_SRCS
miniwin/misc_msg.cpp
mpq/mpq_reader.cpp
mpq/mpq_sdl_rwops.cpp
mpq/mpq_writer.cpp
panels/charpanel.cpp
panels/info_box.cpp
panels/mainpanel.cpp
@ -168,6 +164,14 @@ set(libdevilutionx_SRCS
utils/surface_to_clx.cpp
utils/utf8.cpp)
if(NOT (UNPACKED_MPQS AND UNPACKED_SAVES))
list(APPEND libdevilutionx_DEPS libmpq)
list(APPEND libdevilutionx_SRCS
mpq/mpq_reader.cpp
mpq/mpq_sdl_rwops.cpp
mpq/mpq_writer.cpp)
endif()
if(IOS)
list(APPEND libdevilutionx_SRCS platform/ios/ios_paths.m)
endif()
@ -251,11 +255,11 @@ target_link_libraries(libdevilutionx PUBLIC
DevilutionX::SDL
fmt::fmt
PKWare
libmpq
libsmackerdec
simpleini::simpleini
tl
hoehrmann_utf8
${libdevilutionx_DEPS}
)
if(NOT USE_SDL1)

5
Source/engine/assets.cpp

@ -5,12 +5,15 @@
#include <cstring>
#include "init.h"
#include "mpq/mpq_sdl_rwops.hpp"
#include "utils/file_util.h"
#include "utils/log.hpp"
#include "utils/paths.h"
#include "utils/str_cat.hpp"
#ifndef UNPACKED_MPQS
#include "mpq/mpq_sdl_rwops.hpp"
#endif
namespace devilution {
namespace {

5
Source/init.cpp

@ -18,7 +18,6 @@
#include "engine/dx.h"
#include "hwcursor.hpp"
#include "miniwin/misc_msg.h"
#include "mpq/mpq_reader.hpp"
#include "options.h"
#include "pfile.h"
#include "utils/file_util.h"
@ -29,6 +28,10 @@
#include "utils/ui_fwd.h"
#include "utils/utf8.hpp"
#ifndef UNPACKED_MPQS
#include "mpq/mpq_reader.hpp"
#endif
#ifdef __vita__
// increase default allowed heap size on Vita
int _newlib_heap_size_user = 100 * 1024 * 1024;

6
Source/init.h

@ -6,8 +6,12 @@
#pragma once
#include "miniwin/misc_msg.h"
#include "mpq/mpq_reader.hpp"
#include "utils/attributes.h"
#include "utils/stdcompat/optional.hpp"
#ifndef UNPACKED_MPQS
#include "mpq/mpq_reader.hpp"
#endif
namespace devilution {

26
Source/loadsave.cpp

@ -28,7 +28,7 @@
#include "menu.h"
#include "missiles.h"
#include "monster.h"
#include "mpq/mpq_writer.hpp"
#include "mpq/mpq_common.hpp"
#include "pfile.h"
#include "qol/stash.h"
#include "stores.h"
@ -97,7 +97,7 @@ class LoadHelper {
}
public:
LoadHelper(std::optional<MpqArchive> archive, const char *szFileName)
LoadHelper(std::optional<SaveReader> archive, const char *szFileName)
{
if (archive)
m_buffer_ = ReadArchive(*archive, szFileName, &m_size_);
@ -163,14 +163,14 @@ public:
};
class SaveHelper {
MpqWriter &m_mpqWriter;
SaveWriter &m_mpqWriter;
const char *m_szFileName_;
std::unique_ptr<byte[]> m_buffer_;
size_t m_cur_ = 0;
size_t m_capacity_;
public:
SaveHelper(MpqWriter &mpqWriter, const char *szFileName, size_t bufferLen)
SaveHelper(SaveWriter &mpqWriter, const char *szFileName, size_t bufferLen)
: m_mpqWriter(mpqWriter)
, m_szFileName_(szFileName)
, m_buffer_(new byte[codec_get_encoded_len(bufferLen)])
@ -913,7 +913,7 @@ void GetPermLevelNames(char *szPerm)
return GetLevelNames("perm", szPerm);
}
bool LevelFileExists(MpqWriter &archive)
bool LevelFileExists(SaveWriter &archive)
{
char szName[MaxMpqPathSize];
@ -1647,7 +1647,7 @@ void SaveDroppedItemLocations(SaveHelper &file, const std::unordered_map<uint8_t
constexpr uint32_t VersionAdditionalMissiles = 0;
void SaveAdditionalMissiles(MpqWriter &saveWriter)
void SaveAdditionalMissiles(SaveWriter &saveWriter)
{
constexpr size_t BytesWrittenBySaveMissile = 180;
uint32_t missileCountAdditional = (Missiles.size() > MaxMissilesForSaveGame) ? static_cast<uint32_t>(Missiles.size() - MaxMissilesForSaveGame) : 0;
@ -1691,7 +1691,7 @@ const int HellfireItemSaveSize = 372;
} // namespace
void ConvertLevels(MpqWriter &saveWriter)
void ConvertLevels(SaveWriter &saveWriter)
{
// Backup current level state
bool tmpSetlevel = setlevel;
@ -1934,7 +1934,7 @@ void LoadHotkeys()
myPlayer._pRSplType = static_cast<spell_type>(file.NextLE<uint8_t>());
}
void SaveHotkeys(MpqWriter &saveWriter, const Player &player)
void SaveHotkeys(SaveWriter &saveWriter, const Player &player)
{
SaveHelper file(saveWriter, "hotkeys", HotkeysSize());
@ -2221,7 +2221,7 @@ void LoadGame(bool firstflag)
gbIsHellfireSaveGame = gbIsHellfire;
}
void SaveHeroItems(MpqWriter &saveWriter, Player &player)
void SaveHeroItems(SaveWriter &saveWriter, Player &player)
{
size_t itemCount = static_cast<size_t>(NUM_INVLOC) + InventoryGridCells + MaxBeltItems;
SaveHelper file(saveWriter, "heroitems", itemCount * (gbIsHellfire ? HellfireItemSaveSize : DiabloItemSaveSize) + sizeof(uint8_t));
@ -2236,7 +2236,7 @@ void SaveHeroItems(MpqWriter &saveWriter, Player &player)
SaveItem(file, item);
}
void SaveStash(MpqWriter &stashWriter)
void SaveStash(SaveWriter &stashWriter)
{
const char *filename;
if (!gbIsMultiplayer)
@ -2293,7 +2293,7 @@ void SaveStash(MpqWriter &stashWriter)
file.WriteLE<uint32_t>(static_cast<uint32_t>(Stash.GetPage()));
}
void SaveGameData(MpqWriter &saveWriter)
void SaveGameData(SaveWriter &saveWriter)
{
SaveHelper file(saveWriter, "game", 320 * 1024);
@ -2462,7 +2462,7 @@ void SaveGame()
sfile_write_stash();
}
void SaveLevel(MpqWriter &saveWriter)
void SaveLevel(SaveWriter &saveWriter)
{
Player &myPlayer = *MyPlayer;
@ -2539,7 +2539,7 @@ void SaveLevel(MpqWriter &saveWriter)
void LoadLevel()
{
char szName[MaxMpqPathSize];
std::optional<MpqArchive> archive = OpenSaveArchive(gSaveNumber);
std::optional<SaveReader> archive = OpenSaveArchive(gSaveNumber);
GetTempLevelNames(szName);
if (!archive || !archive->HasFile(szName))
GetPermLevelNames(szName);

14
Source/loadsave.h

@ -5,7 +5,7 @@
*/
#pragma once
#include "mpq/mpq_writer.hpp"
#include "pfile.h"
#include "player.h"
#include "utils/attributes.h"
@ -33,14 +33,14 @@ void RemoveEmptyInventory(Player &player);
* @param firstflag Can be set to false if we are simply reloading the current game
*/
void LoadGame(bool firstflag);
void SaveHotkeys(MpqWriter &saveWriter, const Player &player);
void SaveHeroItems(MpqWriter &saveWriter, Player &player);
void SaveGameData(MpqWriter &saveWriter);
void SaveHotkeys(SaveWriter &saveWriter, const Player &player);
void SaveHeroItems(SaveWriter &saveWriter, Player &player);
void SaveGameData(SaveWriter &saveWriter);
void SaveGame();
void SaveLevel(MpqWriter &saveWriter);
void SaveLevel(SaveWriter &saveWriter);
void LoadLevel();
void ConvertLevels(MpqWriter &saveWriter);
void ConvertLevels(SaveWriter &saveWriter);
void LoadStash();
void SaveStash(MpqWriter &stashWriter);
void SaveStash(SaveWriter &stashWriter);
} // namespace devilution

4
Source/mpq/mpq_writer.hpp

@ -15,6 +15,10 @@ namespace devilution {
class MpqWriter {
public:
explicit MpqWriter(const char *path);
explicit MpqWriter(const std::string &path)
: MpqWriter(path.c_str())
{
}
MpqWriter(MpqWriter &&other) = default;
MpqWriter &operator=(MpqWriter &&other) = default;
~MpqWriter();

142
Source/pfile.cpp

@ -17,7 +17,7 @@
#include "init.h"
#include "loadsave.h"
#include "menu.h"
#include "mpq/mpq_reader.hpp"
#include "mpq/mpq_common.hpp"
#include "pack.h"
#include "qol/stash.h"
#include "utils/endian.hpp"
@ -29,6 +29,12 @@
#include "utils/str_cat.hpp"
#include "utils/utf8.hpp"
#ifdef UNPACKED_SAVES
#include "utils/file_util.h"
#else
#include "mpq/mpq_reader.hpp"
#endif
namespace devilution {
#define PASSWORD_SPAWN_SINGLE "adslhfb1"
@ -49,14 +55,25 @@ std::string GetSavePath(uint32_t saveNum, string_view savePrefix = {})
gbIsSpawn
? (gbIsMultiplayer ? "share_" : "spawn_")
: (gbIsMultiplayer ? "multi_" : "single_"),
saveNum, gbIsHellfire ? ".hsv" : ".sv");
saveNum,
#ifdef UNPACKED_SAVES
gbIsHellfire ? "_hsv" DIRECTORY_SEPARATOR_STR : "_sv" DIRECTORY_SEPARATOR_STR
#else
gbIsHellfire ? ".hsv" : ".sv"
#endif
);
}
std::string GetStashSavePath()
{
return StrCat(paths::PrefPath(),
gbIsSpawn ? "stash_spawn" : "stash",
gbIsHellfire ? ".hsv" : ".sv");
#ifdef UNPACKED_SAVES
gbIsHellfire ? "_hsv" DIRECTORY_SEPARATOR_STR : "_sv" DIRECTORY_SEPARATOR_STR
#else
gbIsHellfire ? ".hsv" : ".sv"
#endif
);
}
bool GetSaveNames(uint8_t index, string_view prefix, char *out)
@ -85,7 +102,7 @@ bool GetTempSaveNames(uint8_t dwIndex, char *szTemp)
return GetSaveNames(dwIndex, "temp", szTemp);
}
void RenameTempToPerm(MpqWriter &saveWriter)
void RenameTempToPerm(SaveWriter &saveWriter)
{
char szTemp[MaxMpqPathSize];
char szPerm[MaxMpqPathSize];
@ -104,7 +121,7 @@ void RenameTempToPerm(MpqWriter &saveWriter)
assert(!GetPermSaveNames(dwIndex, szPerm));
}
bool ReadHero(MpqArchive &archive, PlayerPack *pPack)
bool ReadHero(SaveReader &archive, PlayerPack *pPack)
{
size_t read;
@ -121,7 +138,7 @@ bool ReadHero(MpqArchive &archive, PlayerPack *pPack)
return ret;
}
void EncodeHero(MpqWriter &saveWriter, const PlayerPack *pack)
void EncodeHero(SaveWriter &saveWriter, const PlayerPack *pack)
{
size_t packedLen = codec_get_encoded_len(sizeof(*pack));
std::unique_ptr<byte[]> packed { new byte[packedLen] };
@ -131,14 +148,14 @@ void EncodeHero(MpqWriter &saveWriter, const PlayerPack *pack)
saveWriter.WriteFile("hero", packed.get(), packedLen);
}
MpqWriter GetSaveWriter(uint32_t saveNum)
SaveWriter GetSaveWriter(uint32_t saveNum)
{
return MpqWriter(GetSavePath(saveNum).c_str());
return SaveWriter(GetSavePath(saveNum));
}
MpqWriter GetStashWriter()
SaveWriter GetStashWriter()
{
return MpqWriter(GetStashSavePath().c_str());
return SaveWriter(GetStashSavePath());
}
#ifndef DISABLE_DEMOMODE
@ -191,7 +208,7 @@ bool GetFileName(uint8_t lvl, char *dst)
return false;
}
bool ArchiveContainsGame(MpqArchive &hsArchive)
bool ArchiveContainsGame(SaveReader &hsArchive)
{
if (gbIsMultiplayer)
return false;
@ -205,6 +222,18 @@ bool ArchiveContainsGame(MpqArchive &hsArchive)
return IsHeaderValid(hdr);
}
std::optional<SaveReader> CreateSaveReader(std::string &&path)
{
#ifdef UNPACKED_SAVES
if (!FileExists(path))
return std::nullopt;
return SaveReader(std::move(path));
#else
std::int32_t error;
return MpqArchive::Open(path.c_str(), error);
#endif
}
#ifndef DISABLE_DEMOMODE
class MemoryBuffer : public std::basic_streambuf<char> {
public:
@ -425,9 +454,8 @@ HeroCompareResult CompareSaves(const std::string &actualSavePath, const std::str
possibleFileToCheck.push_back({ std::string(szPerm), "level", i == 0 });
}
std::int32_t error;
auto actualArchive = *MpqArchive::Open(actualSavePath.c_str(), error);
auto referenceArchive = *MpqArchive::Open(referenceSavePath.c_str(), error);
SaveReader actualArchive = *CreateSaveReader(std::string(actualSavePath));
SaveReader referenceArchive = *CreateSaveReader(std::string(referenceSavePath));
bool compareResult = true;
std::string message;
@ -466,7 +494,7 @@ HeroCompareResult CompareSaves(const std::string &actualSavePath, const std::str
}
#endif // !DISABLE_DEMOMODE
void pfile_write_hero(MpqWriter &saveWriter, bool writeGameData)
void pfile_write_hero(SaveWriter &saveWriter, bool writeGameData)
{
if (writeGameData) {
SaveGameData(saveWriter);
@ -485,19 +513,69 @@ void pfile_write_hero(MpqWriter &saveWriter, bool writeGameData)
} // namespace
std::optional<MpqArchive> OpenSaveArchive(uint32_t saveNum)
#ifdef UNPACKED_SAVES
std::unique_ptr<byte[]> SaveReader::ReadFile(const char *filename, std::size_t &fileSize, int32_t &error)
{
std::int32_t error;
return MpqArchive::Open(GetSavePath(saveNum).c_str(), error);
std::unique_ptr<byte[]> result;
error = 0;
const std::string path = dir_ + filename;
uintmax_t size;
if (!GetFileSize(path.c_str(), &size)) {
error = 1;
return nullptr;
}
fileSize = size;
FILE *file = OpenFile(path.c_str(), "rb");
if (file == nullptr) {
error = 1;
return nullptr;
}
result.reset(new byte[size]);
if (std::fread(result.get(), size, 1, file) != 1) {
std::fclose(file);
error = 1;
return nullptr;
}
std::fclose(file);
return result;
}
std::optional<MpqArchive> OpenStashArchive()
bool SaveWriter::WriteFile(const char *filename, const byte *data, size_t size)
{
std::int32_t error;
return MpqArchive::Open(GetStashSavePath().c_str(), error);
const std::string path = dir_ + filename;
FILE *file = OpenFile(path.c_str(), "wb");
if (file == nullptr) {
return false;
}
if (std::fwrite(data, size, 1, file) != 1) {
std::fclose(file);
return false;
}
std::fclose(file);
return true;
}
void SaveWriter::RemoveHashEntries(bool (*fnGetName)(uint8_t, char *))
{
char pszFileName[MaxMpqPathSize];
for (uint8_t i = 0; fnGetName(i, pszFileName); i++) {
RemoveHashEntry(pszFileName);
}
}
#endif
std::optional<SaveReader> OpenSaveArchive(uint32_t saveNum)
{
return CreateSaveReader(GetSavePath(saveNum));
}
std::optional<SaveReader> OpenStashArchive()
{
return CreateSaveReader(GetStashSavePath());
}
std::unique_ptr<byte[]> ReadArchive(MpqArchive &archive, const char *pszName, size_t *pdwLen)
std::unique_ptr<byte[]> ReadArchive(SaveReader &archive, const char *pszName, size_t *pdwLen)
{
int32_t error;
std::size_t length;
@ -525,7 +603,7 @@ const char *pfile_get_password()
void pfile_write_hero(bool writeGameData)
{
MpqWriter saveWriter = GetSaveWriter(gSaveNumber);
SaveWriter saveWriter = GetSaveWriter(gSaveNumber);
pfile_write_hero(saveWriter, writeGameData);
}
@ -534,7 +612,7 @@ void pfile_write_hero_demo(int demo)
{
std::string savePath = GetSavePath(gSaveNumber, StrCat("demo_", demo, "_reference_"));
CopySaveFile(gSaveNumber, savePath);
auto saveWriter = MpqWriter(savePath.c_str());
auto saveWriter = SaveWriter(savePath.c_str());
pfile_write_hero(saveWriter, true);
}
@ -548,7 +626,7 @@ HeroCompareResult pfile_compare_hero_demo(int demo, bool logDetails)
std::string actualSavePath = GetSavePath(gSaveNumber, StrCat("demo_", demo, "_actual_"));
{
CopySaveFile(gSaveNumber, actualSavePath);
MpqWriter saveWriter(actualSavePath.c_str());
SaveWriter saveWriter(actualSavePath.c_str());
pfile_write_hero(saveWriter, true);
}
@ -561,7 +639,7 @@ void sfile_write_stash()
if (!Stash.dirty)
return;
MpqWriter stashWriter = GetStashWriter();
SaveWriter stashWriter = GetStashWriter();
SaveStash(stashWriter);
@ -573,7 +651,7 @@ bool pfile_ui_set_hero_infos(bool (*uiAddHeroInfo)(_uiheroinfo *))
memset(hero_names, 0, sizeof(hero_names));
for (uint32_t i = 0; i < MAX_CHARACTERS; i++) {
std::optional<MpqArchive> archive = OpenSaveArchive(i);
std::optional<SaveReader> archive = OpenSaveArchive(i);
if (archive) {
PlayerPack pkplr;
if (ReadHero(*archive, &pkplr)) {
@ -632,7 +710,7 @@ bool pfile_ui_save_create(_uiheroinfo *heroinfo)
giNumberOfLevels = gbIsHellfire ? 25 : 17;
MpqWriter saveWriter = GetSaveWriter(saveNum);
SaveWriter saveWriter = GetSaveWriter(saveNum);
saveWriter.RemoveHashEntries(GetFileName);
CopyUtf8(hero_names[saveNum], heroinfo->name, sizeof(hero_names[saveNum]));
@ -666,7 +744,7 @@ void pfile_read_player_from_save(uint32_t saveNum, Player &player)
PlayerPack pkplr;
{
std::optional<MpqArchive> archive = OpenSaveArchive(saveNum);
std::optional<SaveReader> archive = OpenSaveArchive(saveNum);
if (!archive)
app_fatal(_("Unable to open archive"));
if (!ReadHero(*archive, &pkplr))
@ -688,13 +766,13 @@ void pfile_read_player_from_save(uint32_t saveNum, Player &player)
void pfile_save_level()
{
MpqWriter saveWriter = GetSaveWriter(gSaveNumber);
SaveWriter saveWriter = GetSaveWriter(gSaveNumber);
SaveLevel(saveWriter);
}
void pfile_convert_levels()
{
MpqWriter saveWriter = GetSaveWriter(gSaveNumber);
SaveWriter saveWriter = GetSaveWriter(gSaveNumber);
ConvertLevels(saveWriter);
}
@ -703,7 +781,7 @@ void pfile_remove_temp_files()
if (gbIsMultiplayer)
return;
MpqWriter saveWriter = GetSaveWriter(gSaveNumber);
SaveWriter saveWriter = GetSaveWriter(gSaveNumber);
saveWriter.RemoveHashEntries(GetTempSaveNames);
}

70
Source/pfile.h

@ -8,12 +8,76 @@
#include "DiabloUI/diabloui.h"
#include "player.h"
#ifdef UNPACKED_SAVES
#include "utils/file_util.h"
#else
#include "mpq/mpq_reader.hpp"
#include "mpq/mpq_writer.hpp"
#endif
namespace devilution {
#define MAX_CHARACTERS 99
extern bool gbValidSaveFile;
#ifdef UNPACKED_SAVES
struct SaveReader {
explicit SaveReader(std::string &&dir)
: dir_(std::move(dir))
{
}
const std::string &dir() const
{
return dir_;
}
std::unique_ptr<byte[]> ReadFile(const char *filename, std::size_t &fileSize, int32_t &error);
bool HasFile(const char *path)
{
return ::devilution::FileExists((dir_ + path).c_str());
}
private:
std::string dir_;
};
struct SaveWriter {
explicit SaveWriter(std::string &&dir)
: dir_(std::move(dir))
{
}
bool WriteFile(const char *filename, const byte *data, size_t size);
bool HasFile(const char *path)
{
return ::devilution::FileExists((dir_ + path).c_str());
}
void RenameFile(const char *from, const char *to)
{
::devilution::RenameFile((dir_ + from).c_str(), (dir_ + to).c_str());
}
void RemoveHashEntry(const char *path)
{
RemoveFile((dir_ + path).c_str());
}
void RemoveHashEntries(bool (*fnGetName)(uint8_t, char *));
private:
std::string dir_;
};
#else
using SaveReader = MpqArchive;
using SaveWriter = MpqWriter;
#endif
/**
* @brief Comparsion result of pfile_compare_hero_demo
*/
@ -27,10 +91,10 @@ struct HeroCompareResult {
std::string message;
};
std::optional<MpqArchive> OpenSaveArchive(uint32_t saveNum);
std::optional<MpqArchive> OpenStashArchive();
std::optional<SaveReader> OpenSaveArchive(uint32_t saveNum);
std::optional<SaveReader> OpenStashArchive();
const char *pfile_get_password();
std::unique_ptr<byte[]> ReadArchive(MpqArchive &archive, const char *pszName, size_t *pdwLen = nullptr);
std::unique_ptr<byte[]> ReadArchive(SaveReader &archive, const char *pszName, size_t *pdwLen = nullptr);
void pfile_write_hero(bool writeGameData = false);
#ifndef DISABLE_DEMOMODE

28
Source/utils/file_util.cpp

@ -6,6 +6,7 @@
#include <SDL.h>
#include "utils/log.hpp"
#include "utils/stdcompat/filesystem.hpp"
#ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
@ -74,6 +75,9 @@ bool FileExists(const char *path)
return true;
#elif (_POSIX_C_SOURCE >= 200112L || defined(_BSD_SOURCE) || defined(__APPLE__)) && !defined(__ANDROID__)
return ::access(path, F_OK) == 0;
#elif defined(DVL_HAS_FILESYSTEM)
std::error_code ec;
return std::filesystem::exists(path, ec);
#else
SDL_RWops *file = SDL_RWFromFile(path, "r+b");
if (file == nullptr)
@ -185,6 +189,26 @@ bool ResizeFile(const char *path, std::uintmax_t size)
#endif
}
void RenameFile(const char *from, const char *to)
{
#if defined(NXDK)
::MoveFile(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;
}
::MoveFileW(&fromUtf16[0], &toUtf16[0]);
#elif defined(DVL_HAS_FILESYSTEM)
std::error_code ec;
return std::filesystem::rename(from, to);
#else
::rename(from, to);
#endif
}
void RemoveFile(const char *path)
{
#if defined(NXDK)
@ -204,9 +228,9 @@ void RemoveFile(const char *path)
fclose(f);
remove(name.c_str());
f = nullptr;
Log("Removed file: {}", name);
LogVerbose("Removed file: {}", name);
} else {
Log("Failed to remove file: {}", name);
LogVerbose("Failed to remove file: {}", name);
}
#endif
}

1
Source/utils/file_util.h

@ -21,6 +21,7 @@ inline bool FileExists(const std::string &str)
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 RemoveFile(const char *path);
std::optional<std::fstream> CreateFileStream(const char *path, std::ios::openmode mode);
FILE *OpenFile(const char *path, const char *mode);

6
Source/utils/paths.cpp

@ -21,12 +21,6 @@
#include "utils/sdl2_to_1_2_backports.h"
#endif
#ifdef _WIN32
#define DIRECTORY_SEPARATOR_STR "\\"
#else
#define DIRECTORY_SEPARATOR_STR "/"
#endif
namespace devilution {
namespace paths {

2
Source/utils/paths.h

@ -8,8 +8,10 @@ namespace devilution {
#ifdef _WIN32
constexpr char DirectorySeparator = '\\';
#define DIRECTORY_SEPARATOR_STR "\\"
#else
constexpr char DirectorySeparator = '/';
#define DIRECTORY_SEPARATOR_STR "/"
#endif
namespace paths {

Loading…
Cancel
Save