Browse Source

Save Methods in loadsave gets MpqWriter passed

pull/4717/head
obligaron 4 years ago committed by Anders Jenbo
parent
commit
7afe7d2a70
  1. 2
      Source/diablo.cpp
  2. 2
      Source/init.cpp
  3. 16
      Source/interfac.cpp
  4. 151
      Source/loadsave.cpp
  5. 12
      Source/loadsave.h
  6. 7
      Source/mpq/mpq_reader.cpp
  7. 2
      Source/mpq/mpq_reader.hpp
  8. 80
      Source/mpq/mpq_writer.cpp
  9. 20
      Source/mpq/mpq_writer.hpp
  10. 133
      Source/pfile.cpp
  11. 23
      Source/pfile.h

2
Source/diablo.cpp

@ -796,7 +796,7 @@ void RunGameLoop(interface_mode uMsg)
demo::NotifyGameLoopEnd();
if (gbIsMultiplayer) {
pfile_write_hero(/*writeGameData=*/false, /*clearTables=*/true);
pfile_write_hero(/*writeGameData=*/false);
sfile_write_stash();
}

2
Source/init.cpp

@ -129,7 +129,7 @@ std::vector<std::string> GetMPQSearchPaths()
void init_cleanup()
{
if (gbIsMultiplayer && gbRunGame) {
pfile_write_hero(/*writeGameData=*/false, /*clearTables=*/true);
pfile_write_hero(/*writeGameData=*/false);
sfile_write_stash();
}

16
Source/interfac.cpp

@ -284,7 +284,7 @@ void ShowProgress(interface_mode uMsg)
case WM_DIABNEXTLVL:
IncProgress();
if (!gbIsMultiplayer) {
SaveLevel();
pfile_save_level();
} else {
DeltaSaveLevel();
}
@ -300,7 +300,7 @@ void ShowProgress(interface_mode uMsg)
case WM_DIABPREVLVL:
IncProgress();
if (!gbIsMultiplayer) {
SaveLevel();
pfile_save_level();
} else {
DeltaSaveLevel();
}
@ -317,7 +317,7 @@ void ShowProgress(interface_mode uMsg)
SetReturnLvlPos();
IncProgress();
if (!gbIsMultiplayer) {
SaveLevel();
pfile_save_level();
} else {
DeltaSaveLevel();
}
@ -332,7 +332,7 @@ void ShowProgress(interface_mode uMsg)
case WM_DIABRTNLVL:
IncProgress();
if (!gbIsMultiplayer) {
SaveLevel();
pfile_save_level();
} else {
DeltaSaveLevel();
}
@ -347,7 +347,7 @@ void ShowProgress(interface_mode uMsg)
case WM_DIABWARPLVL:
IncProgress();
if (!gbIsMultiplayer) {
SaveLevel();
pfile_save_level();
} else {
DeltaSaveLevel();
}
@ -361,7 +361,7 @@ void ShowProgress(interface_mode uMsg)
case WM_DIABTOWNWARP:
IncProgress();
if (!gbIsMultiplayer) {
SaveLevel();
pfile_save_level();
} else {
DeltaSaveLevel();
}
@ -377,7 +377,7 @@ void ShowProgress(interface_mode uMsg)
case WM_DIABTWARPUP:
IncProgress();
if (!gbIsMultiplayer) {
SaveLevel();
pfile_save_level();
} else {
DeltaSaveLevel();
}
@ -392,7 +392,7 @@ void ShowProgress(interface_mode uMsg)
case WM_DIABRETOWN:
IncProgress();
if (!gbIsMultiplayer) {
SaveLevel();
pfile_save_level();
} else {
DeltaSaveLevel();
}

151
Source/loadsave.cpp

@ -877,54 +877,32 @@ void LoadPortal(LoadHelper *file, int i)
pPortal->setlvl = file->NextBool32();
}
void ConvertLevels()
void GetTempLevelNames(char *szTemp)
{
// Backup current level state
bool tmpSetlevel = setlevel;
_setlevels tmpSetlvlnum = setlvlnum;
int tmpCurrlevel = currlevel;
dungeon_type tmpLeveltype = leveltype;
gbSkipSync = true;
setlevel = false; // Convert regular levels
for (int i = 0; i < giNumberOfLevels; i++) {
currlevel = i;
if (!LevelFileExists())
continue;
leveltype = GetLevelType(currlevel);
LoadLevel();
SaveLevel();
}
setlevel = true; // Convert quest levels
for (auto &quest : Quests) {
if (quest._qactive == QUEST_NOTAVAIL) {
continue;
}
leveltype = quest._qlvltype;
if (leveltype == DTYPE_NONE) {
continue;
}
if (setlevel)
sprintf(szTemp, "temps%02d", setlvlnum);
else
sprintf(szTemp, "templ%02d", currlevel);
}
setlvlnum = quest._qslvl;
if (!LevelFileExists())
continue;
void GetPermLevelNames(char *szPerm)
{
if (setlevel)
sprintf(szPerm, "perms%02d", setlvlnum);
else
sprintf(szPerm, "perml%02d", currlevel);
}
LoadLevel();
SaveLevel();
}
bool LevelFileExists(MpqWriter &archive)
{
char szName[MAX_PATH];
gbSkipSync = false;
GetTempLevelNames(szName);
if (archive.HasFile(szName))
return true;
// Restor current level state
setlevel = tmpSetlevel;
setlvlnum = tmpSetlvlnum;
currlevel = tmpCurrlevel;
leveltype = tmpLeveltype;
GetPermLevelNames(szName);
return archive.HasFile(szName);
}
void LoadMatchingItems(LoadHelper &file, const int n, Item *pItem)
@ -1627,11 +1605,11 @@ void SaveDroppedItemLocations(SaveHelper &file, const std::unordered_map<uint8_t
constexpr uint32_t VersionAdditionalMissiles = 0;
void SaveAdditionalMissiles()
void SaveAdditionalMissiles(MpqWriter &saveWriter)
{
constexpr size_t BytesWrittenBySaveMissile = 180;
uint32_t missileCountAdditional = (Missiles.size() > MaxMissilesForSaveGame) ? static_cast<uint32_t>(Missiles.size() - MaxMissilesForSaveGame) : 0;
SaveHelper file(CurrentSaveArchive(), "additionalMissiles", sizeof(uint32_t) + sizeof(uint32_t) + (missileCountAdditional * BytesWrittenBySaveMissile));
SaveHelper file(saveWriter, "additionalMissiles", sizeof(uint32_t) + sizeof(uint32_t) + (missileCountAdditional * BytesWrittenBySaveMissile));
file.WriteLE<uint32_t>(VersionAdditionalMissiles);
file.WriteLE<uint32_t>(missileCountAdditional);
@ -1671,6 +1649,56 @@ const int HellfireItemSaveSize = 372;
} // namespace
void ConvertLevels(MpqWriter &saveWriter)
{
// Backup current level state
bool tmpSetlevel = setlevel;
_setlevels tmpSetlvlnum = setlvlnum;
int tmpCurrlevel = currlevel;
dungeon_type tmpLeveltype = leveltype;
gbSkipSync = true;
setlevel = false; // Convert regular levels
for (int i = 0; i < giNumberOfLevels; i++) {
currlevel = i;
if (!LevelFileExists(saveWriter))
continue;
leveltype = GetLevelType(currlevel);
LoadLevel();
SaveLevel(saveWriter);
}
setlevel = true; // Convert quest levels
for (auto &quest : Quests) {
if (quest._qactive == QUEST_NOTAVAIL) {
continue;
}
leveltype = quest._qlvltype;
if (leveltype == DTYPE_NONE) {
continue;
}
setlvlnum = quest._qslvl;
if (!LevelFileExists(saveWriter))
continue;
LoadLevel();
SaveLevel(saveWriter);
}
gbSkipSync = false;
// Restor current level state
setlevel = tmpSetlevel;
setlvlnum = tmpSetlvlnum;
currlevel = tmpCurrlevel;
leveltype = tmpLeveltype;
}
void RemoveInvalidItem(Item &item)
{
bool isInvalid = !IsItemAvailable(item.IDidx) || !IsUniqueAvailable(item._iUid);
@ -1864,11 +1892,11 @@ void LoadHotkeys()
myPlayer._pRSplType = static_cast<spell_type>(file.NextLE<uint8_t>());
}
void SaveHotkeys()
void SaveHotkeys(MpqWriter &saveWriter)
{
Player &myPlayer = *MyPlayer;
SaveHelper file(CurrentSaveArchive(), "hotkeys", HotkeysSize());
SaveHelper file(saveWriter, "hotkeys", HotkeysSize());
// Write the number of spell hotkeys
file.WriteLE<uint8_t>(static_cast<uint8_t>(NumHotkeys));
@ -2013,7 +2041,7 @@ void LoadGame(bool firstflag)
LoadPortal(&file, i);
if (gbIsHellfireSaveGame != gbIsHellfire) {
ConvertLevels();
pfile_convert_levels();
RemoveEmptyInventory(myPlayer);
}
@ -2153,10 +2181,10 @@ void LoadGame(bool firstflag)
gbIsHellfireSaveGame = gbIsHellfire;
}
void SaveHeroItems(Player &player)
void SaveHeroItems(MpqWriter &saveWriter, Player &player)
{
size_t itemCount = NUM_INVLOC + NUM_INV_GRID_ELEM + MAXBELTITEMS;
SaveHelper file(CurrentSaveArchive(), "heroitems", itemCount * (gbIsHellfire ? HellfireItemSaveSize : DiabloItemSaveSize) + sizeof(uint8_t));
SaveHelper file(saveWriter, "heroitems", itemCount * (gbIsHellfire ? HellfireItemSaveSize : DiabloItemSaveSize) + sizeof(uint8_t));
file.WriteLE<uint8_t>(gbIsHellfire ? 1 : 0);
@ -2168,7 +2196,7 @@ void SaveHeroItems(Player &player)
SaveItem(file, item);
}
void SaveStash()
void SaveStash(MpqWriter &stashWriter)
{
const char *filename;
if (!gbIsMultiplayer)
@ -2179,7 +2207,7 @@ void SaveStash()
const int itemSize = (gbIsHellfire ? HellfireItemSaveSize : DiabloItemSaveSize);
SaveHelper file(
StashArchive(),
stashWriter,
filename,
sizeof(uint8_t)
+ sizeof(uint32_t)
@ -2225,9 +2253,9 @@ void SaveStash()
file.WriteLE<uint32_t>(static_cast<uint32_t>(Stash.GetPage()));
}
void SaveGameData()
void SaveGameData(MpqWriter &saveWriter)
{
SaveHelper file(CurrentSaveArchive(), "game", 320 * 1024);
SaveHelper file(saveWriter, "game", 320 * 1024);
if (gbIsSpawn && !gbIsHellfire)
file.WriteLE<uint32_t>(LoadLE32("SHAR"));
@ -2386,7 +2414,7 @@ void SaveGameData()
file.WriteLE<uint8_t>(AutomapActive ? 1 : 0);
file.WriteBE<int32_t>(AutoMapScale);
SaveAdditionalMissiles();
SaveAdditionalMissiles(saveWriter);
}
void SaveGame()
@ -2396,10 +2424,8 @@ void SaveGame()
sfile_write_stash();
}
void SaveLevel()
void SaveLevel(MpqWriter &saveWriter)
{
PFileScopedArchiveWriter scopedWriter;
Player &myPlayer = *MyPlayer;
DoUnVision(myPlayer.position.tile, myPlayer._pLightRad); // fix for vision staying on the level
@ -2409,7 +2435,7 @@ void SaveLevel()
char szName[MAX_PATH];
GetTempLevelNames(szName);
SaveHelper file(CurrentSaveArchive(), szName, 256 * 1024);
SaveHelper file(saveWriter, szName, 256 * 1024);
if (leveltype != DTYPE_TOWN) {
for (int j = 0; j < MAXDUNY; j++) {
@ -2475,8 +2501,11 @@ void SaveLevel()
void LoadLevel()
{
char szName[MAX_PATH];
GetPermLevelNames(szName);
LoadHelper file(OpenSaveArchive(gSaveNumber), szName);
std::optional<MpqArchive> archive = OpenSaveArchive(gSaveNumber);
GetTempLevelNames(szName);
if (!archive || !archive->HasFile(szName))
GetPermLevelNames(szName);
LoadHelper file(std::move(archive), szName);
if (!file.IsValid())
app_fatal(_("Unable to open save file archive"));

12
Source/loadsave.h

@ -5,6 +5,7 @@
*/
#pragma once
#include "mpq/mpq_writer.hpp"
#include "player.h"
#include "utils/attributes.h"
@ -32,13 +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();
void SaveHeroItems(Player &player);
void SaveGameData();
void SaveHotkeys(MpqWriter &saveWriter);
void SaveHeroItems(MpqWriter &saveWriter, Player &player);
void SaveGameData(MpqWriter &saveWriter);
void SaveGame();
void SaveLevel();
void SaveLevel(MpqWriter &saveWriter);
void LoadLevel();
void ConvertLevels(MpqWriter &saveWriter);
void LoadStash();
void SaveStash();
void SaveStash(MpqWriter &stashWriter);
} // namespace devilution

7
Source/mpq/mpq_reader.cpp

@ -142,4 +142,11 @@ std::size_t MpqArchive::GetBlockSize(uint32_t fileNumber, uint32_t blockNumber,
return blockSize;
}
bool MpqArchive::HasFile(const char *filename) const
{
std::uint32_t fileNumber;
int32_t error = libmpq__file_number(archive_, filename, &fileNumber);
return error == 0;
}
} // namespace devilution

2
Source/mpq/mpq_reader.hpp

@ -59,6 +59,8 @@ public:
// Requires the block offset table to be open
std::size_t GetBlockSize(uint32_t fileNumber, uint32_t blockNumber, int32_t &error);
bool HasFile(const char *filename) const;
private:
MpqArchive(std::string path, mpq_archive_s *archive)
: path_(std::move(path))

80
Source/mpq/mpq_writer.cpp

@ -11,6 +11,7 @@
#include "engine.h"
#include "utils/endian.hpp"
#include "utils/file_util.h"
#include "utils/language.h"
#include "utils/log.hpp"
namespace devilution {
@ -83,16 +84,15 @@ bool IsUnallocatedBlock(const MpqBlockEntry *block)
} // namespace
bool MpqWriter::Open(const char *path)
MpqWriter::MpqWriter(const char *path)
{
Close(/*clearTables=*/false);
LogVerbose("Opening {}", path);
exists_ = FileExists(path);
bool exists = FileExists(path);
std::ios::openmode mode = std::ios::in | std::ios::out | std::ios::binary;
if (exists_) {
if (exists) {
if (!GetFileSize(path, &size_)) {
Log(R"(GetFileSize("{}") failed with "{}")", path, std::strerror(errno));
return false;
goto on_error;
}
LogVerbose("GetFileSize(\"{}\") = {}", path, size_);
} else {
@ -100,37 +100,36 @@ bool MpqWriter::Open(const char *path)
}
if (!stream_.Open(path, mode)) {
stream_.Close();
return false;
goto on_error;
}
modified_ = !exists_;
name_ = path;
if (blockTable_ == nullptr || hashTable_ == nullptr) {
MpqFileHeader fhdr;
if (!exists_) {
if (!exists) {
InitDefaultMpqHeader(&fhdr);
} else if (!ReadMPQHeader(&fhdr)) {
goto on_error;
}
blockTable_ = new MpqBlockEntry[BlockEntriesCount];
std::memset(blockTable_, 0, BlockEntriesCount * sizeof(MpqBlockEntry));
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_), static_cast<std::streamsize>(fhdr.blockEntriesCount * sizeof(MpqBlockEntry))))
if (!stream_.Read(reinterpret_cast<char *>(blockTable_.get()), static_cast<std::streamsize>(fhdr.blockEntriesCount * sizeof(MpqBlockEntry))))
goto on_error;
uint32_t key = Hash("(block table)", 3);
Decrypt(reinterpret_cast<uint32_t *>(blockTable_), fhdr.blockEntriesCount * sizeof(MpqBlockEntry), key);
Decrypt(reinterpret_cast<uint32_t *>(blockTable_.get()), fhdr.blockEntriesCount * sizeof(MpqBlockEntry), key);
}
hashTable_ = new MpqHashEntry[HashEntriesCount];
hashTable_ = std::make_unique<MpqHashEntry[]>(HashEntriesCount);
// We fill with 0xFF so that the `block` field defaults to -1 (a null block pointer).
std::memset(hashTable_, 0xFF, HashEntriesCount * sizeof(MpqHashEntry));
std::memset(hashTable_.get(), 0xFF, HashEntriesCount * sizeof(MpqHashEntry));
if (fhdr.hashEntriesCount > 0) {
if (!stream_.Read(reinterpret_cast<char *>(hashTable_), static_cast<std::streamsize>(fhdr.hashEntriesCount * sizeof(MpqHashEntry))))
if (!stream_.Read(reinterpret_cast<char *>(hashTable_.get()), static_cast<std::streamsize>(fhdr.hashEntriesCount * sizeof(MpqHashEntry))))
goto on_error;
uint32_t key = Hash("(hash table)", 3);
Decrypt(reinterpret_cast<uint32_t *>(hashTable_), fhdr.hashEntriesCount * sizeof(MpqHashEntry), key);
Decrypt(reinterpret_cast<uint32_t *>(hashTable_.get()), fhdr.hashEntriesCount * sizeof(MpqHashEntry), key);
}
#ifndef CAN_SEEKP_BEYOND_EOF
@ -143,38 +142,31 @@ bool MpqWriter::Open(const char *path)
// Write garbage header and tables because some platforms cannot `Seekp` beyond EOF.
// The data is incorrect at this point, it will be overwritten on Close.
if (!exists_)
if (!exists)
WriteHeaderAndTables();
#endif
}
return true;
return;
on_error:
Close(/*clearTables=*/true);
return false;
app_fatal(_("Failed to open archive for writing."));
}
bool MpqWriter::Close(bool clearTables)
MpqWriter::~MpqWriter()
{
if (!stream_.IsOpen())
return true;
LogVerbose("Closing {} with clearTables={}", name_, clearTables);
return;
LogVerbose("Closing {}", name_);
bool result = true;
if (modified_ && !(stream_.Seekp(0, std::ios::beg) && WriteHeaderAndTables()))
if (!(stream_.Seekp(0, std::ios::beg) && WriteHeaderAndTables()))
result = false;
stream_.Close();
if (modified_ && result && size_ != 0) {
if (result && size_ != 0) {
LogVerbose("ResizeFile(\"{}\", {})", name_, size_);
result = ResizeFile(name_.c_str(), size_);
}
name_.clear();
if (clearTables) {
delete[] hashTable_;
hashTable_ = nullptr;
delete[] blockTable_;
blockTable_ = nullptr;
}
return result;
if (!result)
LogVerbose("Closing failed {}", name_);
}
uint32_t MpqWriter::FetchHandle(const char *filename) const
@ -190,7 +182,6 @@ void MpqWriter::InitDefaultMpqHeader(MpqFileHeader *hdr)
hdr->blockSizeFactor = BlockSizeFactor;
hdr->version = 0;
size_ = MpqHashEntryOffset + HashEntrySize;
modified_ = true;
}
bool MpqWriter::IsValidMpqHeader(MpqFileHeader *hdr) const
@ -222,7 +213,7 @@ bool MpqWriter::ReadMPQHeader(MpqFileHeader *hdr)
MpqBlockEntry *MpqWriter::NewBlock(uint32_t *blockIndex)
{
MpqBlockEntry *blockEntry = blockTable_;
MpqBlockEntry *blockEntry = blockTable_.get();
for (unsigned i = 0; i < BlockEntriesCount; ++i, ++blockEntry) {
if (!IsUnallocatedBlock(blockEntry))
@ -242,7 +233,7 @@ void MpqWriter::AllocBlock(uint32_t blockOffset, uint32_t blockSize)
MpqBlockEntry *block;
bool expand;
do {
block = blockTable_;
block = blockTable_.get();
expand = false;
for (unsigned i = BlockEntriesCount; i-- != 0; ++block) {
// Expand to adjacent blocks.
@ -282,7 +273,7 @@ uint32_t MpqWriter::FindFreeBlock(uint32_t size)
{
uint32_t result;
MpqBlockEntry *block = blockTable_;
MpqBlockEntry *block = blockTable_.get();
for (unsigned i = 0; i < BlockEntriesCount; ++i, ++block) {
// Find a block entry to use space from.
if (!IsAllocatedUnusedBlock(block) || block->packedSize < size)
@ -464,17 +455,17 @@ bool MpqWriter::WriteHeader()
bool MpqWriter::WriteBlockTable()
{
Encrypt(reinterpret_cast<uint32_t *>(blockTable_), BlockEntrySize, Hash("(block table)", 3));
const bool success = stream_.Write(reinterpret_cast<const char *>(blockTable_), BlockEntrySize);
Decrypt(reinterpret_cast<uint32_t *>(blockTable_), BlockEntrySize, Hash("(block table)", 3));
Encrypt(reinterpret_cast<uint32_t *>(blockTable_.get()), BlockEntrySize, Hash("(block table)", 3));
const bool success = stream_.Write(reinterpret_cast<const char *>(blockTable_.get()), BlockEntrySize);
Decrypt(reinterpret_cast<uint32_t *>(blockTable_.get()), BlockEntrySize, Hash("(block table)", 3));
return success;
}
bool MpqWriter::WriteHashTable()
{
Encrypt(reinterpret_cast<uint32_t *>(hashTable_), HashEntrySize, Hash("(hash table)", 3));
const bool success = stream_.Write(reinterpret_cast<const char *>(hashTable_), HashEntrySize);
Decrypt(reinterpret_cast<uint32_t *>(hashTable_), HashEntrySize, Hash("(hash table)", 3));
Encrypt(reinterpret_cast<uint32_t *>(hashTable_.get()), HashEntrySize, Hash("(hash table)", 3));
const bool success = stream_.Write(reinterpret_cast<const char *>(hashTable_.get()), HashEntrySize);
Decrypt(reinterpret_cast<uint32_t *>(hashTable_.get()), HashEntrySize, Hash("(hash table)", 3));
return success;
}
@ -492,7 +483,6 @@ void MpqWriter::RemoveHashEntry(const char *filename)
const uint32_t blockSize = block->packedSize;
memset(block, 0, sizeof(*block));
AllocBlock(blockOffset, blockSize);
modified_ = true;
}
void MpqWriter::RemoveHashEntries(bool (*fnGetName)(uint8_t, char *))
@ -508,7 +498,6 @@ bool MpqWriter::WriteFile(const char *filename, const byte *data, size_t size)
{
MpqBlockEntry *blockEntry;
modified_ = true;
RemoveHashEntry(filename);
blockEntry = AddFile(filename, nullptr, 0);
if (!WriteFileContents(filename, data, size, blockEntry)) {
@ -530,7 +519,6 @@ void MpqWriter::RenameFile(const char *name, const char *newName) // NOLINT(bugp
MpqBlockEntry *blockEntry = &blockTable_[block];
hashEntry->block = MpqHashEntry::DeletedBlock;
AddFile(newName, blockEntry, block);
modified_ = true;
}
bool MpqWriter::HasFile(const char *name) const

20
Source/mpq/mpq_writer.hpp

@ -14,14 +14,10 @@
namespace devilution {
class MpqWriter {
public:
bool Open(const char *path);
bool Close(bool clearTables = true);
~MpqWriter()
{
Close();
}
explicit MpqWriter(const char *path);
MpqWriter(MpqWriter &&other) = default;
MpqWriter &operator=(MpqWriter &&other) = default;
~MpqWriter();
bool HasFile(const char *name) const;
@ -56,11 +52,9 @@ private:
LoggedFStream stream_;
std::string name_;
std::uintmax_t size_;
bool modified_;
bool exists_;
MpqHashEntry *hashTable_;
MpqBlockEntry *blockTable_;
std::uintmax_t size_ {};
std::unique_ptr<MpqHashEntry[]> hashTable_;
std::unique_ptr<MpqBlockEntry[]> blockTable_;
// Amiga cannot Seekp beyond EOF.
// See https://github.com/bebbo/libnix/issues/30

133
Source/pfile.cpp

@ -32,9 +32,6 @@ bool gbValidSaveFile;
namespace {
MpqWriter SaveWriter;
MpqWriter StashWriter;
/** List of character names for the character selection screen. */
char hero_names[MAX_CHARACTERS][PLR_NAME_LEN];
@ -118,7 +115,7 @@ bool GetTempSaveNames(uint8_t dwIndex, char *szTemp)
return true;
}
void RenameTempToPerm()
void RenameTempToPerm(MpqWriter &saveWriter)
{
char szTemp[MAX_PATH];
char szPerm[MAX_PATH];
@ -128,10 +125,10 @@ void RenameTempToPerm()
[[maybe_unused]] bool result = GetPermSaveNames(dwIndex, szPerm); // DO NOT PUT DIRECTLY INTO ASSERT!
assert(result);
dwIndex++;
if (SaveWriter.HasFile(szTemp)) {
if (SaveWriter.HasFile(szPerm))
SaveWriter.RemoveHashEntry(szPerm);
SaveWriter.RenameFile(szTemp, szPerm);
if (saveWriter.HasFile(szTemp)) {
if (saveWriter.HasFile(szPerm))
saveWriter.RemoveHashEntry(szPerm);
saveWriter.RenameFile(szTemp, szPerm);
}
}
assert(!GetPermSaveNames(dwIndex, szPerm));
@ -154,19 +151,24 @@ bool ReadHero(MpqArchive &archive, PlayerPack *pPack)
return ret;
}
void EncodeHero(const PlayerPack *pack)
void EncodeHero(MpqWriter &saveWriter, const PlayerPack *pack)
{
size_t packedLen = codec_get_encoded_len(sizeof(*pack));
std::unique_ptr<byte[]> packed { new byte[packedLen] };
memcpy(packed.get(), pack, sizeof(*pack));
codec_encode(packed.get(), sizeof(*pack), packedLen, pfile_get_password());
SaveWriter.WriteFile("hero", packed.get(), packedLen);
saveWriter.WriteFile("hero", packed.get(), packedLen);
}
MpqWriter GetSaveWriter(uint32_t saveNum)
{
return MpqWriter(GetSavePath(saveNum).c_str());
}
bool OpenArchive(uint32_t saveNum)
MpqWriter GetStashWriter()
{
return SaveWriter.Open(GetSavePath(saveNum).c_str());
return MpqWriter(GetStashSavePath().c_str());
}
void Game2UiPlayer(const Player &player, _uiheroinfo *heroinfo, bool bHasSaveFile)
@ -298,51 +300,28 @@ const char *pfile_get_password()
return gbIsMultiplayer ? PASSWORD_MULTI : PASSWORD_SINGLE;
}
PFileScopedArchiveWriter::PFileScopedArchiveWriter(bool clearTables)
: save_num_(gSaveNumber)
, clear_tables_(clearTables)
{
if (!OpenArchive(save_num_))
app_fatal(_("Failed to open player archive for writing."));
}
PFileScopedArchiveWriter::~PFileScopedArchiveWriter()
{
SaveWriter.Close(clear_tables_);
}
MpqWriter &CurrentSaveArchive()
{
return SaveWriter;
}
MpqWriter &StashArchive()
void pfile_write_hero(bool writeGameData)
{
return StashWriter;
}
void pfile_write_hero(bool writeGameData, bool clearTables)
{
PFileScopedArchiveWriter scopedWriter(clearTables);
MpqWriter saveWriter = GetSaveWriter(gSaveNumber);
if (writeGameData) {
SaveGameData();
RenameTempToPerm();
SaveGameData(saveWriter);
RenameTempToPerm(saveWriter);
}
PlayerPack pkplr;
Player &myPlayer = *MyPlayer;
PackPlayer(&pkplr, myPlayer, !gbIsMultiplayer, false);
EncodeHero(&pkplr);
EncodeHero(saveWriter, &pkplr);
if (!gbVanilla) {
SaveHotkeys();
SaveHeroItems(myPlayer);
SaveHotkeys(saveWriter);
SaveHeroItems(saveWriter, myPlayer);
}
}
void pfile_write_hero_demo(int demo)
{
savePrefix = fmt::format("demo_{}_reference_", demo);
pfile_write_hero(true, true);
pfile_write_hero(true);
savePrefix.clear();
}
@ -356,7 +335,7 @@ HeroCompareResult pfile_compare_hero_demo(int demo)
return HeroCompareResult::ReferenceNotFound;
savePrefix = fmt::format("demo_{}_actual_", demo);
pfile_write_hero(true, true);
pfile_write_hero(true);
std::string actualSavePath = GetSavePath(gSaveNumber);
savePrefix.clear();
@ -369,12 +348,9 @@ void sfile_write_stash()
if (!Stash.dirty)
return;
if (!StashWriter.Open(GetStashSavePath().c_str()))
app_fatal(_("Failed to open stash archive for writing."));
SaveStash();
MpqWriter stashWriter = GetStashWriter();
StashWriter.Close();
SaveStash(stashWriter);
Stash.dirty = false;
}
@ -439,27 +415,25 @@ bool pfile_ui_save_create(_uiheroinfo *heroinfo)
uint32_t saveNum = heroinfo->saveNumber;
if (saveNum >= MAX_CHARACTERS)
return false;
if (!OpenArchive(saveNum))
return false;
heroinfo->saveNumber = saveNum;
giNumberOfLevels = gbIsHellfire ? 25 : 17;
SaveWriter.RemoveHashEntries(GetFileName);
MpqWriter saveWriter = GetSaveWriter(saveNum);
saveWriter.RemoveHashEntries(GetFileName);
CopyUtf8(hero_names[saveNum], heroinfo->name, sizeof(hero_names[saveNum]));
Player &player = Players[0];
CreatePlayer(0, heroinfo->heroclass);
CopyUtf8(player._pName, heroinfo->name, PLR_NAME_LEN);
PackPlayer(&pkplr, player, true, false);
EncodeHero(&pkplr);
EncodeHero(saveWriter, &pkplr);
Game2UiPlayer(player, heroinfo, false);
if (!gbVanilla) {
SaveHotkeys();
SaveHeroItems(player);
SaveHotkeys(saveWriter);
SaveHeroItems(saveWriter, player);
}
SaveWriter.Close();
return true;
}
@ -499,44 +473,16 @@ void pfile_read_player_from_save(uint32_t saveNum, Player &player)
CalcPlrInv(player, false);
}
bool LevelFileExists()
void pfile_save_level()
{
char szName[MAX_PATH];
GetPermLevelNames(szName);
uint32_t saveNum = gSaveNumber;
if (!OpenArchive(saveNum))
app_fatal(_("Unable to read to save file archive"));
bool hasFile = SaveWriter.HasFile(szName);
SaveWriter.Close();
return hasFile;
}
void GetTempLevelNames(char *szTemp)
{
if (setlevel)
sprintf(szTemp, "temps%02d", setlvlnum);
else
sprintf(szTemp, "templ%02d", currlevel);
MpqWriter saveWriter = GetSaveWriter(gSaveNumber);
SaveLevel(saveWriter);
}
void GetPermLevelNames(char *szPerm)
void pfile_convert_levels()
{
uint32_t saveNum = gSaveNumber;
GetTempLevelNames(szPerm);
if (!OpenArchive(saveNum))
app_fatal(_("Unable to read to save file archive"));
bool hasFile = SaveWriter.HasFile(szPerm);
SaveWriter.Close();
if (!hasFile) {
if (setlevel)
sprintf(szPerm, "perms%02d", setlvlnum);
else
sprintf(szPerm, "perml%02d", currlevel);
}
MpqWriter saveWriter = GetSaveWriter(gSaveNumber);
ConvertLevels(saveWriter);
}
void pfile_remove_temp_files()
@ -544,11 +490,8 @@ void pfile_remove_temp_files()
if (gbIsMultiplayer)
return;
uint32_t saveNum = gSaveNumber;
if (!OpenArchive(saveNum))
app_fatal(_("Unable to write to save file archive"));
SaveWriter.RemoveHashEntries(GetTempSaveNames);
SaveWriter.Close();
MpqWriter saveWriter = GetSaveWriter(gSaveNumber);
saveWriter.RemoveHashEntries(GetTempSaveNames);
}
void pfile_update(bool forceSave)

23
Source/pfile.h

@ -6,7 +6,6 @@
#pragma once
#include "DiabloUI/diabloui.h"
#include "mpq/mpq_writer.hpp"
#include "player.h"
namespace devilution {
@ -15,19 +14,6 @@ namespace devilution {
extern bool gbValidSaveFile;
class PFileScopedArchiveWriter {
public:
// Opens the player save file for writing
PFileScopedArchiveWriter(bool clearTables = !gbIsMultiplayer);
// Finishes writing and closes the player save file.
~PFileScopedArchiveWriter();
private:
int save_num_;
bool clear_tables_;
};
/**
* @brief Comparsion result of pfile_compare_hero_demo
*/
@ -37,13 +23,11 @@ enum class HeroCompareResult {
Difference,
};
MpqWriter &CurrentSaveArchive();
MpqWriter &StashArchive();
std::optional<MpqArchive> OpenSaveArchive(uint32_t saveNum);
std::optional<MpqArchive> OpenStashArchive();
const char *pfile_get_password();
std::unique_ptr<byte[]> ReadArchive(MpqArchive &archive, const char *pszName, size_t *pdwLen = nullptr);
void pfile_write_hero(bool writeGameData = false, bool clearTables = !gbIsMultiplayer);
void pfile_write_hero(bool writeGameData = false);
/**
* @brief Save a reference game-state (save game) for the demo recording
* @param demo that is recorded
@ -62,9 +46,8 @@ uint32_t pfile_ui_get_first_unused_save_num();
bool pfile_ui_save_create(_uiheroinfo *heroinfo);
bool pfile_delete_save(_uiheroinfo *heroInfo);
void pfile_read_player_from_save(uint32_t saveNum, Player &player);
bool LevelFileExists();
void GetTempLevelNames(char *szTemp);
void GetPermLevelNames(char *szPerm);
void pfile_save_level();
void pfile_convert_levels();
void pfile_remove_temp_files();
std::unique_ptr<byte[]> pfile_read(const char *pszName, size_t *pdwLen);
void pfile_update(bool forceSave);

Loading…
Cancel
Save