diff --git a/targets/minecraft/world/level/storage/ConsoleSaveFileIO/ConsoleSaveFileSplit.cpp b/targets/minecraft/world/level/storage/ConsoleSaveFileIO/ConsoleSaveFileSplit.cpp index 92645bd2f..a25bc93d7 100644 --- a/targets/minecraft/world/level/storage/ConsoleSaveFileIO/ConsoleSaveFileSplit.cpp +++ b/targets/minecraft/world/level/storage/ConsoleSaveFileIO/ConsoleSaveFileSplit.cpp @@ -216,11 +216,13 @@ void ConsoleSaveFileSplit::RegionFileReference::Decompress() { unsigned char* dataIn = dataCompressed + 4; unsigned char* dataInLast = dataCompressed + dataCompressedSize; - while (dataIn != dataInLast) { + while (dataIn < dataInLast) { unsigned char thisByte = *dataIn++; if (thisByte == 0) { + if (dataIn >= dataInLast) break; thisByte = *dataIn++; if (thisByte == 0) { + if (dataIn + 1 >= dataInLast) break; unsigned int runLength = (*dataIn++) << 8; runLength |= (*dataIn++); runLength += 256; @@ -237,7 +239,6 @@ void ConsoleSaveFileSplit::RegionFileReference::Decompress() { if (fileEntry->data.length != uncompressedSize) { // Treat as if it was an empty region file fileEntry->data.length = 0; - assert(0); return; } } @@ -246,21 +247,24 @@ void ConsoleSaveFileSplit::RegionFileReference::Decompress() { unsigned char* dataIn = dataCompressed + 4; unsigned char* dataInLast = dataCompressed + dataCompressedSize; unsigned char* dataOut = data; + unsigned char* dataOutLast = data + fileEntry->data.length; - while (dataIn != dataInLast) { + while (dataIn < dataInLast && dataOut < dataOutLast) { unsigned char thisByte = *dataIn++; if (thisByte == 0) { + if (dataIn >= dataInLast) break; thisByte = *dataIn++; if (thisByte == 0) { + if (dataIn + 1 >= dataInLast) break; unsigned int runLength = (*dataIn++) << 8; runLength |= (*dataIn++); runLength += 256; - for (unsigned int i = 0; i < runLength; i++) { + for (unsigned int i = 0; i < runLength && dataOut < dataOutLast; i++) { *dataOut++ = 0; } } else { unsigned int runLength = thisByte; - for (unsigned int i = 0; i < runLength; i++) { + for (unsigned int i = 0; i < runLength && dataOut < dataOutLast; i++) { *dataOut++ = 0; } } @@ -274,7 +278,6 @@ void ConsoleSaveFileSplit::RegionFileReference::Decompress() { free(data); fileEntry->data.length = 0; data = nullptr; - assert(0); } // std::int64_t endTime = System::currentTimeMillis(); // app.DebugPrintf("Decompressing region file from 0x%.8x %d to %d bytes - @@ -437,6 +440,7 @@ void ConsoleSaveFileSplit::_init(const std::wstring& fileName, void* pvSaveData, // Get details of region files. From this point on we are responsible for // the memory that the storage manager initially allocated for them unsigned int regionCount = PlatformStorage.GetSubfileCount(); + for (unsigned int i = 0; i < regionCount; i++) { unsigned int regionIndex; unsigned char* regionDataCompressed; @@ -446,8 +450,14 @@ void ConsoleSaveFileSplit::_init(const std::wstring& fileName, void* pvSaveData, (void**)®ionDataCompressed, ®ionSizeCompressed); + unsigned char* copiedData = nullptr; + if (regionSizeCompressed > 0 && regionDataCompressed) { + copiedData = (unsigned char*)malloc(regionSizeCompressed); + memcpy(copiedData, regionDataCompressed, regionSizeCompressed); + } + RegionFileReference* regionFileRef = new RegionFileReference( - i, regionIndex, regionSizeCompressed, regionDataCompressed); + regionIndex, regionIndex, regionSizeCompressed, copiedData); if (regionSizeCompressed > 0) { regionFileRef->Decompress(); } else { @@ -493,10 +503,12 @@ void ConsoleSaveFileSplit::_init(const std::wstring& fileName, void* pvSaveData, PlatformStorage.GetSaveData(pvSaveMem, &storageLength); app.DebugPrintf("Filesize - %d, Adjusted size - %d\n", fileSize, storageLength); + fileSize = storageLength; } int compressed = *(int*)pvSaveMem; + if (compressed == 0) { unsigned int decompSize = *((int*)pvSaveMem + 1); @@ -1288,6 +1300,7 @@ void ConsoleSaveFileSplit::Flush(bool autosave, bool updateThumbnail) { unsigned int fileSize = header.GetFileSize(); + // Assume that the compression will make it smaller so initially attempt to // allocate the current file size We add 4 bytes to the start so that we can // signal compressed data And another 4 bytes to store the decompressed data diff --git a/targets/minecraft/world/level/storage/ConsoleSaveFileIO/FileHeader.cpp b/targets/minecraft/world/level/storage/ConsoleSaveFileIO/FileHeader.cpp index 129c81425..6b753859d 100644 --- a/targets/minecraft/world/level/storage/ConsoleSaveFileIO/FileHeader.cpp +++ b/targets/minecraft/world/level/storage/ConsoleSaveFileIO/FileHeader.cpp @@ -203,6 +203,7 @@ void FileHeader::ReadHeader( // based PlayerUID // : Bumped it to 8 for Durango v1 when to save the chunks in a // different compressed format + case SAVE_FILE_VERSION_CHUNK_INHABITED_TIME: case SAVE_FILE_VERSION_COMPRESSED_CHUNK_STORAGE: case SAVE_FILE_VERSION_DURANGO_CHANGE_MAP_DATA_MAPPING_SIZE: case SAVE_FILE_VERSION_CHANGE_MAP_DATA_MAPPING_SIZE: diff --git a/targets/platform/sdl2/Storage.cpp b/targets/platform/sdl2/Storage.cpp index c0bff4a1b..d0d3733f1 100644 --- a/targets/platform/sdl2/Storage.cpp +++ b/targets/platform/sdl2/Storage.cpp @@ -33,8 +33,11 @@ static unsigned int s_SaveBufferSize = 0; struct SubfileData { std::vector data; }; + +#include static std::map s_Subfiles; static int s_SubfileCounter = 0; +static std::mutex s_SubfileMutex; // helper functions static StdFileIO s_FileIO; @@ -87,8 +90,11 @@ void C4JStorage::Init(unsigned int uiSaveVersion, void C4JStorage::ResetSaveData() { s_CurrentSaveTitle = L"New World"; s_CurrentSaveFilename = ""; - s_Subfiles.clear(); - s_SubfileCounter = 0; + { + std::lock_guard lock(s_SubfileMutex); + s_Subfiles.clear(); + s_SubfileCounter = 0; + } if (s_SaveBuffer) { free(s_SaveBuffer); s_SaveBuffer = nullptr; @@ -314,19 +320,33 @@ C4JStorage::ESaveGameState C4JStorage::LoadSaveData( s_SaveBufferSize = (unsigned int)blobData.size(); s_SaveBuffer = (std::uint8_t*)malloc(s_SaveBufferSize); memcpy(s_SaveBuffer, blobData.data(), s_SaveBufferSize); + } // put in mem - s_Subfiles.clear(); - for (int i = 0; i < 50; i++) { - auto subData = s_FileIO.readFileToVec( - GetSaveFile(s_CurrentSaveFilename, - "subfile_" + std::to_string(i) + ".dat")); - if (!subData.empty()) { - SubfileData sd; - sd.data = std::move(subData); - s_Subfiles[i] = std::move(sd); + { + std::lock_guard lock(s_SubfileMutex); + s_Subfiles.clear(); + int maxIndex = -1; + auto dir = GetSaveDir(s_CurrentSaveFilename); + std::error_code ec; + for (const auto& entry : std::filesystem::directory_iterator(dir, ec)) { + if (!entry.is_regular_file()) continue; + std::string filename = entry.path().filename().string(); + if (filename.find("subfile_") == 0 && filename.find(".dat") == filename.size() - 4) { + try { + int i = std::stoi(filename.substr(8, filename.size() - 12)); + auto subData = s_FileIO.readFileToVec(entry.path()); + if (!subData.empty()) { + SubfileData sd; + sd.data = std::move(subData); + s_Subfiles[i] = std::move(sd); + if (i > maxIndex) maxIndex = i; + } + } catch (...) {} + } } + s_SubfileCounter = maxIndex + 1; } } @@ -426,17 +446,30 @@ unsigned int C4JStorage::CRC(unsigned char* buf, int len) { // that fucking bird that i hate int C4JStorage::AddSubfile(int regionIndex) { - s_Subfiles[s_SubfileCounter] = SubfileData(); - return s_SubfileCounter++; + std::lock_guard lock(s_SubfileMutex); + s_Subfiles[regionIndex] = SubfileData(); + if (regionIndex >= s_SubfileCounter) { + s_SubfileCounter = regionIndex + 1; + } + return regionIndex; } -unsigned int C4JStorage::GetSubfileCount() { return s_Subfiles.size(); } +unsigned int C4JStorage::GetSubfileCount() { + std::lock_guard lock(s_SubfileMutex); + return s_Subfiles.size(); +} void C4JStorage::GetSubfileDetails(unsigned int i, int* regionIndex, void** data, unsigned int* size) { - auto it = s_Subfiles.find(i); + std::lock_guard lock(s_SubfileMutex); + auto it = s_Subfiles.begin(); + unsigned int index = 0; + while (it != s_Subfiles.end() && index < i) { + ++it; + ++index; + } if (it != s_Subfiles.end()) { - if (regionIndex) *regionIndex = 0; + if (regionIndex) *regionIndex = it->first; if (data) *data = it->second.data.data(); if (size) *size = (unsigned int)it->second.data.size(); } else { @@ -447,15 +480,18 @@ void C4JStorage::GetSubfileDetails(unsigned int i, int* regionIndex, } void C4JStorage::ResetSubfiles() { + std::lock_guard lock(s_SubfileMutex); s_Subfiles.clear(); s_SubfileCounter = 0; } void C4JStorage::UpdateSubfile(int index, void* data, unsigned int size) { + std::lock_guard lock(s_SubfileMutex); SubfileData& sd = s_Subfiles[index]; // inserts if missing sd.data.resize(size); memcpy(sd.data.data(), data, size); } void C4JStorage::SaveSubfiles(std::function callback) { + std::lock_guard lock(s_SubfileMutex); if (!s_CurrentSaveFilename.empty() && !s_Subfiles.empty()) { auto dir = GetSaveDir(s_CurrentSaveFilename); std::error_code ec;