restructure codebase according to vcproj filters

This commit is contained in:
Tropical
2026-03-30 09:50:58 -05:00
parent d5cf90c713
commit 451682693e
3015 changed files with 46858 additions and 54635 deletions

View File

@@ -0,0 +1,61 @@
#pragma once
#include "FileHeader.h"
#include "ConsoleSavePath.h"
enum class SaveFileSeekOrigin { Begin, Current, End };
class ConsoleSaveFile {
public:
virtual ~ConsoleSaveFile() {};
virtual FileEntry* createFile(const ConsoleSavePath& fileName) = 0;
virtual void deleteFile(FileEntry* file) = 0;
virtual void setFilePointer(FileEntry* file, unsigned int distanceToMove,
SaveFileSeekOrigin seekOrigin) = 0;
virtual bool writeFile(FileEntry* file, const void* lpBuffer,
unsigned int nNumberOfBytesToWrite,
unsigned int* lpNumberOfBytesWritten) = 0;
virtual bool zeroFile(FileEntry* file, unsigned int nNumberOfBytesToWrite,
unsigned int* lpNumberOfBytesWritten) = 0;
virtual bool readFile(FileEntry* file, void* lpBuffer,
unsigned int nNumberOfBytesToRead,
unsigned int* lpNumberOfBytesRead) = 0;
virtual bool closeHandle(FileEntry* file) = 0;
virtual void finalizeWrite() = 0;
virtual void tick() {};
virtual bool doesFileExist(ConsoleSavePath file) = 0;
virtual void Flush(bool autosave, bool updateThumbnail = true) = 0;
#if !defined(_CONTENT_PACKAGE)
virtual void DebugFlushToFile(void* compressedData = nullptr,
unsigned int compressedDataSize = 0) = 0;
#endif
virtual unsigned int getSizeOnDisk() = 0;
virtual std::wstring getFilename() = 0;
virtual std::vector<FileEntry*>* getFilesWithPrefix(
const std::wstring& prefix) = 0;
virtual std::vector<FileEntry*>* getRegionFilesByDimension(
unsigned int dimensionIndex) = 0;
virtual int getSaveVersion() = 0;
virtual int getOriginalSaveVersion() = 0;
virtual void LockSaveAccess() = 0;
virtual void ReleaseSaveAccess() = 0;
virtual ESavePlatform getSavePlatform() = 0;
virtual bool isSaveEndianDifferent() = 0;
virtual void setLocalPlatform() = 0;
virtual void setPlatform(ESavePlatform plat) = 0;
virtual ByteOrder getSaveEndian() = 0;
virtual ByteOrder getLocalEndian() = 0;
virtual void setEndian(ByteOrder endian) = 0;
virtual void ConvertRegionFile(File sourceFile) = 0;
virtual void ConvertToLocalPlatform() = 0;
virtual void* getWritePointer(FileEntry* file) { return nullptr; }
};

View File

@@ -0,0 +1,291 @@
#include "../../Header Files/stdafx.h"
#include "../../net/minecraft/world/level/chunk/storage/net.minecraft.world.level.chunk.storage.h"
#include "../../net/minecraft/world/level/storage/net.minecraft.world.level.storage.h"
#include "ConsoleSaveFileIO.h"
#include "ConsoleSaveFileConverter.h"
#include "../../net/minecraft/util/ProgressListener.h"
void ConsoleSaveFileConverter::ProcessSimpleFile(ConsoleSaveFile* sourceSave,
FileEntry* sourceFileEntry,
ConsoleSaveFile* targetSave,
FileEntry* targetFileEntry) {
unsigned int numberOfBytesRead = 0;
unsigned int numberOfBytesWritten = 0;
std::uint8_t* data = new std::uint8_t[sourceFileEntry->getFileSize()];
// Read from source
sourceSave->readFile(sourceFileEntry, data, sourceFileEntry->getFileSize(),
&numberOfBytesRead);
// Write back to target
targetSave->writeFile(targetFileEntry, data, numberOfBytesRead,
&numberOfBytesWritten);
delete[] data;
}
void ConsoleSaveFileConverter::ProcessStandardRegionFile(
ConsoleSaveFile* sourceSave, File sourceFile, ConsoleSaveFile* targetSave,
File targetFile) {
unsigned int numberOfBytesWritten = 0;
unsigned int numberOfBytesRead = 0;
RegionFile sourceRegionFile(sourceSave, &sourceFile);
RegionFile targetRegionFile(targetSave, &targetFile);
for (unsigned int x = 0; x < 32; ++x) {
for (unsigned int z = 0; z < 32; ++z) {
DataInputStream* dis =
sourceRegionFile.getChunkDataInputStream(x, z);
if (dis) {
int read = dis->read();
DataOutputStream* dos =
targetRegionFile.getChunkDataOutputStream(x, z);
while (read != -1) {
dos->write(read & 0xff);
read = dis->read();
}
dos->close();
dos->deleteChildStream();
delete dos;
}
delete dis;
}
}
}
void ConsoleSaveFileConverter::ConvertSave(ConsoleSaveFile* sourceSave,
ConsoleSaveFile* targetSave,
ProgressListener* progress) {
// Process level.dat
ConsoleSavePath ldatPath(std::wstring(L"level.dat"));
FileEntry* sourceLdatFe = sourceSave->createFile(ldatPath);
FileEntry* targetLdatFe = targetSave->createFile(ldatPath);
printf("Processing level.dat\n");
ProcessSimpleFile(sourceSave, sourceLdatFe, targetSave, targetLdatFe);
// Process game rules
{
ConsoleSavePath gameRulesPath(GAME_RULE_SAVENAME);
if (sourceSave->doesFileExist(gameRulesPath)) {
FileEntry* sourceFe = sourceSave->createFile(gameRulesPath);
FileEntry* targetFe = targetSave->createFile(gameRulesPath);
printf("Processing game rules\n");
ProcessSimpleFile(sourceSave, sourceFe, targetSave, targetFe);
}
}
// MGH added - find any player data files and copy them across
std::vector<FileEntry*>* playerFiles =
sourceSave->getFilesWithPrefix(DirectoryLevelStorage::getPlayerDir());
if (playerFiles != nullptr) {
for (int fileIdx = 0; fileIdx < playerFiles->size(); fileIdx++) {
ConsoleSavePath sourcePlayerDatPath(
playerFiles->at(fileIdx)->data.filename);
ConsoleSavePath targetPlayerDatPath(
playerFiles->at(fileIdx)->data.filename);
{
FileEntry* sourceFe =
sourceSave->createFile(sourcePlayerDatPath);
FileEntry* targetFe =
targetSave->createFile(targetPlayerDatPath);
wprintf(L"Processing player dat file %ls\n",
playerFiles->at(fileIdx)->data.filename);
ProcessSimpleFile(sourceSave, sourceFe, targetSave, targetFe);
targetFe->data.lastModifiedTime =
sourceFe->data.lastModifiedTime;
}
}
delete playerFiles;
}
#if defined(SPLIT_SAVES)
int xzSize = LEVEL_LEGACY_WIDTH;
int hellScale = HELL_LEVEL_LEGACY_SCALE;
if (sourceSave->doesFileExist(ldatPath)) {
ConsoleSaveFileInputStream fis =
ConsoleSaveFileInputStream(sourceSave, ldatPath);
CompoundTag* root = NbtIo::readCompressed(&fis);
CompoundTag* tag = root->getCompound(L"Data");
LevelData ret(tag);
xzSize = ret.getXZSize();
hellScale = ret.getHellScale();
delete root;
}
RegionFileCache sourceCache;
RegionFileCache targetCache;
if (progress) {
progress->progressStage(IDS_SAVETRANSFER_STAGE_CONVERTING);
}
// Overworld
{
printf("Processing the overworld\n");
int halfXZSize = xzSize / 2;
int progressTarget = (xzSize) * (xzSize);
int currentProgress = 0;
if (progress)
progress->progressStagePercentage((currentProgress * 100) /
progressTarget);
for (int x = -halfXZSize; x < halfXZSize; ++x) {
for (int z = -halfXZSize; z < halfXZSize; ++z) {
// printf("Processing overworld chunk %d,%d\n",x,z);
DataInputStream* dis =
sourceCache._getChunkDataInputStream(sourceSave, L"", x, z);
if (dis) {
int read = dis->read();
DataOutputStream* dos =
targetCache._getChunkDataOutputStream(targetSave, L"",
x, z);
BufferedOutputStream bos(dos, 1024 * 1024);
while (read != -1) {
bos.write(read & 0xff);
read = dis->read();
}
bos.flush();
dos->close();
dos->deleteChildStream();
delete dos;
}
delete dis;
++currentProgress;
if (progress)
progress->progressStagePercentage((currentProgress * 100) /
progressTarget);
}
}
}
// Nether
{
printf("Processing the nether\n");
int hellSize = xzSize / hellScale;
int halfXZSize = hellSize / 2;
int progressTarget = (hellSize) * (hellSize);
int currentProgress = 0;
if (progress)
progress->progressStagePercentage((currentProgress * 100) /
progressTarget);
for (int x = -halfXZSize; x < halfXZSize; ++x) {
for (int z = -halfXZSize; z < halfXZSize; ++z) {
// printf("Processing nether chunk %d,%d\n",x,z);
DataInputStream* dis = sourceCache._getChunkDataInputStream(
sourceSave, L"DIM-1", x, z);
if (dis) {
int read = dis->read();
DataOutputStream* dos =
targetCache._getChunkDataOutputStream(targetSave,
L"DIM-1", x, z);
BufferedOutputStream bos(dos, 1024 * 1024);
while (read != -1) {
bos.write(read & 0xff);
read = dis->read();
}
bos.flush();
dos->close();
dos->deleteChildStream();
delete dos;
}
delete dis;
++currentProgress;
if (progress)
progress->progressStagePercentage((currentProgress * 100) /
progressTarget);
}
}
}
// End
{
printf("Processing the end\n");
int halfXZSize = END_LEVEL_MAX_WIDTH / 2;
int progressTarget = (END_LEVEL_MAX_WIDTH) * (END_LEVEL_MAX_WIDTH);
int currentProgress = 0;
if (progress)
progress->progressStagePercentage((currentProgress * 100) /
progressTarget);
for (int x = -halfXZSize; x < halfXZSize; ++x) {
for (int z = -halfXZSize; z < halfXZSize; ++z) {
// printf("Processing end chunk %d,%d\n",x,z);
DataInputStream* dis = sourceCache._getChunkDataInputStream(
sourceSave, L"DIM1/", x, z);
if (dis) {
int read = dis->read();
DataOutputStream* dos =
targetCache._getChunkDataOutputStream(targetSave,
L"DIM1/", x, z);
BufferedOutputStream bos(dos, 1024 * 1024);
while (read != -1) {
bos.write(read & 0xff);
read = dis->read();
}
bos.flush();
dos->close();
dos->deleteChildStream();
delete dos;
}
delete dis;
++currentProgress;
if (progress)
progress->progressStagePercentage((currentProgress * 100) /
progressTarget);
}
}
}
#else
// 4J Stu - Old version that just changes the compression of chunks, not
// usable for XboxOne style split saves or compressed tile formats Process
// region files
std::vector<FileEntry*>* allFilesInSave =
sourceSave->getFilesWithPrefix(std::wstring(L""));
for (auto it = allFilesInSave->begin(); it < allFilesInSave->end(); ++it) {
FileEntry* fe = *it;
if (fe != sourceLdatFe) {
std::wstring fName(fe->data.filename);
std::wstring suffix(L".mcr");
if (fName.compare(fName.length() - suffix.length(), suffix.length(),
suffix) == 0) {
#if !defined(_CONTENT_PACKAGE)
wprintf(L"Processing a region file: %s\n", fe->data.filename);
#endif
ProcessStandardRegionFile(sourceSave, File(fe->data.filename),
targetSave, File(fe->data.filename));
} else {
#if !defined(_CONTENT_PACKAGE)
wprintf(L"%s is not a region file, ignoring\n",
fe->data.filename);
#endif
}
}
}
#endif
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "../../ConsoleJavaLibs/File.h"
class FileEntry;
class ConsoleSaveFile;
class ProgressRenderer;
// 4J Stu - This code is taken from the standalone save converter tool, and
// modified slightly
class ConsoleSaveFileConverter {
private:
static void ProcessSimpleFile(ConsoleSaveFile* sourceSave,
FileEntry* sourceFileEntry,
ConsoleSaveFile* targetSave,
FileEntry* targetFileEntry);
static void ProcessStandardRegionFile(ConsoleSaveFile* sourceSave,
File sourceFile,
ConsoleSaveFile* targetSave,
File targetFile);
public:
static void ConvertSave(ConsoleSaveFile* sourceSave,
ConsoleSaveFile* targetSave,
ProgressListener* progress);
};

View File

@@ -0,0 +1,7 @@
#pragma once
#include "FileHeader.h"
#include "ConsoleSaveFile.h"
#include "ConsoleSaveFileInputStream.h"
#include "ConsoleSaveFileOutputStream.h"
#include "ConsoleSavePath.h"

View File

@@ -0,0 +1,120 @@
#include "../../Header Files/stdafx.h"
#include "../../ConsoleJavaLibs/File.h"
#include "ConsoleSaveFile.h"
#include "ConsoleSaveFileInputStream.h"
ConsoleSaveFileInputStream::ConsoleSaveFileInputStream(
ConsoleSaveFile* saveFile, const ConsoleSavePath& file) {
m_saveFile = saveFile;
m_file = m_saveFile->createFile(file);
m_saveFile->setFilePointer(m_file, 0, SaveFileSeekOrigin::Begin);
}
ConsoleSaveFileInputStream::ConsoleSaveFileInputStream(
ConsoleSaveFile* saveFile, FileEntry* file) {
m_saveFile = saveFile;
m_file = file;
m_saveFile->setFilePointer(m_file, 0, SaveFileSeekOrigin::Begin);
}
// Reads a byte of data from this input stream. This method blocks if no input
// is yet available. Returns: the next byte of data, or -1 if the end of the
// file is reached.
int ConsoleSaveFileInputStream::read() {
std::uint8_t byteRead = static_cast<std::uint8_t>(0);
unsigned int numberOfBytesRead;
bool result =
m_saveFile->readFile(m_file,
&byteRead, // data buffer
1, // number of bytes to read
&numberOfBytesRead // number of bytes read
);
if (!result) {
// TODO 4J Stu - Some kind of error handling
return -1;
} else if (numberOfBytesRead == 0) {
// File pointer is past the end of the file
return -1;
}
return static_cast<int>(byteRead);
}
// Reads up to b.length bytes of data from this input stream into an array of
// bytes. This method blocks until some input is available. Parameters: b - the
// buffer into which the data is read. Returns: the total number of bytes read
// into the buffer, or -1 if there is no more data because the end of the file
// has been reached.
int ConsoleSaveFileInputStream::read(byteArray b) {
unsigned int numberOfBytesRead;
bool result =
m_saveFile->readFile(m_file,
&b.data, // data buffer
b.length, // number of bytes to read
&numberOfBytesRead // number of bytes read
);
if (!result) {
// TODO 4J Stu - Some kind of error handling
return -1;
} else if (numberOfBytesRead == 0) {
// File pointer is past the end of the file
return -1;
}
return numberOfBytesRead;
}
// Reads up to len bytes of data from this input stream into an array of bytes.
// If len is not zero, the method blocks until some input is available;
// otherwise, no bytes are read and 0 is returned. Parameters: b - the buffer
// into which the data is read. off - the start offset in the destination array
// b len - the maximum number of bytes read. Returns: the total number of bytes
// read into the buffer, or -1 if there is no more data because the end of the
// file has been reached.
int ConsoleSaveFileInputStream::read(byteArray b, unsigned int offset,
unsigned int length) {
// 4J Stu - We don't want to read any more than the array buffer can hold
assert(length <= (b.length - offset));
unsigned int numberOfBytesRead;
bool result =
m_saveFile->readFile(m_file,
&b[offset], // data buffer
length, // number of bytes to read
&numberOfBytesRead // number of bytes read
);
if (!result) {
// TODO 4J Stu - Some kind of error handling
return -1;
} else if (numberOfBytesRead == 0) {
// File pointer is past the end of the file
return -1;
}
return numberOfBytesRead;
}
// Closes this file input stream and releases any system resources associated
// with the stream. If this stream has an associated channel then the channel is
// closed as well.
void ConsoleSaveFileInputStream::close() {
if (m_saveFile != nullptr) {
bool result = m_saveFile->closeHandle(m_file);
if (!result) {
// TODO 4J Stu - Some kind of error handling
}
// Stop the dtor from trying to close it again
m_saveFile = nullptr;
}
}

View File

@@ -0,0 +1,26 @@
#pragma once
// 4J Stu - Implements the Java InputStream but rather than writing directly to
// disc it writes through the save file
#include "../../ConsoleJavaLibs/InputOutputStream/InputStream.h"
#include "ConsoleSavePath.h"
class ConsoleSaveFile;
class FileEntry;
class ConsoleSaveFileInputStream : public InputStream {
public:
ConsoleSaveFileInputStream(ConsoleSaveFile* saveFile,
const ConsoleSavePath& file);
ConsoleSaveFileInputStream(ConsoleSaveFile* saveFile, FileEntry* file);
virtual int read();
virtual int read(byteArray b);
virtual int read(byteArray b, unsigned int offset, unsigned int length);
virtual void close();
virtual int64_t skip(int64_t n) { return n; }
private:
ConsoleSaveFile* m_saveFile;
FileEntry* m_file;
};

View File

@@ -0,0 +1,875 @@
#include <thread>
#include <chrono>
#include "../../Header Files/stdafx.h"
#include "../StringHelpers.h"
#include "../../Header Files/PortableFileIO.h"
#include "ConsoleSaveFileOriginal.h"
#include "../../ConsoleJavaLibs/File.h"
#include <chrono>
#include <thread>
#include <xuiapp.h>
#include "../../Header Files/compression.h"
#include "../../../Minecraft.Client/net/minecraft/client/Minecraft.h"
#include "../../../Minecraft.Client/net/minecraft/server/MinecraftServer.h"
#include "../../../Minecraft.Client/net/minecraft/server/level/ServerLevel.h"
#include "../../net/minecraft/world/level/net.minecraft.world.level.h"
#include "../../net/minecraft/world/level/storage/LevelData.h"
#include "../../../Minecraft.Client/Common/Source Files/GameRules/LevelGeneration/LevelGenerationOptions.h"
#include "../../net/minecraft/world/level/chunk/storage/net.minecraft.world.level.chunk.storage.h"
#define RESERVE_ALLOCATION MEM_RESERVE
#define COMMIT_ALLOCATION MEM_COMMIT
unsigned int ConsoleSaveFileOriginal::pagesCommitted = 0;
void* ConsoleSaveFileOriginal::pvHeap = nullptr;
ConsoleSaveFileOriginal::ConsoleSaveFileOriginal(
const std::wstring& fileName, void* pvSaveData /*= nullptr*/,
unsigned int initialFileSize /*= 0*/, bool forceCleanSave /*= false*/,
ESavePlatform plat /*= SAVE_FILE_PLATFORM_LOCAL*/) {
// One time initialise of static stuff required for our storage
if (pvHeap == nullptr) {
// Reserve a chunk of 64MB of virtual address space for our saves, using
// 64KB pages. We'll only be committing these as required to grow the
// storage we need, which will the storage to grow without having to use
// realloc.
// AP - The Vita doesn't have virtual memory so a pretend system has
// been implemented in PSVitaStubs.cpp. All access to the memory must be
// done via the access function as the pointer returned from
// VirtualAlloc can't be used directly.
pvHeap = VirtualAlloc(nullptr, MAX_PAGE_COUNT * CSF_PAGE_SIZE,
RESERVE_ALLOCATION, PAGE_READWRITE);
}
pvSaveMem = pvHeap;
m_fileName = fileName;
unsigned int fileSize = initialFileSize;
// Load a save from the game rules
bool bLevelGenBaseSave = false;
LevelGenerationOptions* levelGen = app.getLevelGenerationOptions();
if (pvSaveData == nullptr && levelGen != nullptr &&
levelGen->requiresBaseSave()) {
pvSaveData = levelGen->getBaseSaveData(fileSize);
if (pvSaveData && fileSize != 0) bLevelGenBaseSave = true;
}
if (pvSaveData == nullptr || fileSize == 0)
fileSize = StorageManager.GetSaveSize();
if (forceCleanSave) fileSize = 0;
unsigned int heapSize = std::max(
fileSize,
1024u * 1024u * 2u); // 4J Stu - Our files are going to be bigger than
// 2MB so allocate high to start with
// Initially committ enough room to store headSize bytes (using
// CSF_PAGE_SIZE pages, so rounding up here). We should only ever have one
// save file at a time, and the pages should be decommitted in the dtor, so
// pages committed should always be zero at this point.
if (pagesCommitted != 0) {
#ifndef _CONTENT_PACKAGE
__debugbreak();
#endif
}
unsigned int pagesRequired =
(heapSize + (CSF_PAGE_SIZE - 1)) / CSF_PAGE_SIZE;
void* pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE,
COMMIT_ALLOCATION, PAGE_READWRITE);
if (pvRet == nullptr) {
#ifndef _CONTENT_PACKAGE
// Out of physical memory
__debugbreak();
#endif
}
pagesCommitted = pagesRequired;
if (fileSize > 0) {
if (pvSaveData != nullptr) {
memcpy(pvSaveMem, pvSaveData, fileSize);
if (bLevelGenBaseSave) {
levelGen->deleteBaseSaveData();
}
} else {
unsigned int storageLength;
StorageManager.GetSaveData(pvSaveMem, &storageLength);
app.DebugPrintf("Filesize - %d, Adjusted size - %d\n", fileSize,
storageLength);
fileSize = storageLength;
}
void* pvSourceData = pvSaveMem;
int compressed = *(int*)pvSourceData;
if (compressed == 0) {
unsigned int decompSize = *((int*)pvSourceData + 1);
if (isLocalEndianDifferent(plat)) System::ReverseULONG(&decompSize);
// An invalid save, so clear the memory and start from scratch
if (decompSize == 0) {
// 4J Stu - Saves created between 2/12/2011 and 7/12/2011
// will have this problem
app.DebugPrintf("Invalid save data format\n");
std::memset(pvSourceData, 0, fileSize);
// Clear the first 8 bytes that reference the header
header.WriteHeader(pvSourceData);
} else {
unsigned char* buf = new unsigned char[decompSize];
Compression::getCompression()->SetDecompressionType(
plat); // if this save is from another platform, set the
// correct decompression type
Compression::getCompression()->Decompress(
buf, &decompSize, (unsigned char*)pvSourceData + 8,
fileSize - 8);
Compression::getCompression()->SetDecompressionType(
SAVE_FILE_PLATFORM_LOCAL); // and then set the
// decompression back to the
// local machine's standard type
// Only ReAlloc if we need to (we might already have enough)
// and align to 512 byte boundaries
unsigned int currentHeapSize = pagesCommitted * CSF_PAGE_SIZE;
unsigned int desiredSize = decompSize;
if (desiredSize > currentHeapSize) {
unsigned int pagesRequired =
(desiredSize + (CSF_PAGE_SIZE - 1)) / CSF_PAGE_SIZE;
void* pvRet =
VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE,
COMMIT_ALLOCATION, PAGE_READWRITE);
if (pvRet == nullptr) {
// Out of physical memory
__debugbreak();
}
pagesCommitted = pagesRequired;
}
memcpy(pvSaveMem, buf, decompSize);
delete[] buf;
}
}
header.ReadHeader(pvSaveMem, plat);
} else {
// Clear the first 8 bytes that reference the header
header.WriteHeader(pvSaveMem);
}
}
ConsoleSaveFileOriginal::~ConsoleSaveFileOriginal() {
VirtualFree(pvHeap, MAX_PAGE_COUNT * CSF_PAGE_SIZE, MEM_DECOMMIT);
pagesCommitted = 0;
}
// Add the file to our table of internal files if not already there
// Open our actual save file ready for reading/writing, and the set the file
// pointer to the start of this file
FileEntry* ConsoleSaveFileOriginal::createFile(
const ConsoleSavePath& fileName) {
LockSaveAccess();
FileEntry* file = header.AddFile(fileName.getName());
ReleaseSaveAccess();
return file;
}
void ConsoleSaveFileOriginal::deleteFile(FileEntry* file) {
if (file == nullptr) return;
LockSaveAccess();
unsigned int numberOfBytesRead = 0;
unsigned int numberOfBytesWritten = 0;
const int bufferSize = 4096;
int amountToRead = bufferSize;
std::uint8_t buffer[bufferSize];
unsigned int bufferDataSize = 0;
char* readStartOffset =
(char*)pvSaveMem + file->data.startOffset + file->getFileSize();
char* writeStartOffset = (char*)pvSaveMem + file->data.startOffset;
char* endOfDataOffset = (char*)pvSaveMem + header.GetStartOfNextData();
while (true) {
// Fill buffer from file
if (readStartOffset + bufferSize > endOfDataOffset) {
amountToRead = (int)(endOfDataOffset - readStartOffset);
} else {
amountToRead = bufferSize;
}
if (amountToRead == 0) break;
memcpy(buffer, readStartOffset, amountToRead);
numberOfBytesRead = amountToRead;
bufferDataSize = amountToRead;
readStartOffset += numberOfBytesRead;
// Write buffer to file
memcpy((void*)writeStartOffset, buffer, bufferDataSize);
numberOfBytesWritten = bufferDataSize;
writeStartOffset += numberOfBytesWritten;
}
header.RemoveFile(file);
finalizeWrite();
ReleaseSaveAccess();
}
void ConsoleSaveFileOriginal::setFilePointer(FileEntry* file,
unsigned int distanceToMove,
SaveFileSeekOrigin seekOrigin) {
LockSaveAccess();
switch (seekOrigin) {
case SaveFileSeekOrigin::Current:
file->currentFilePointer += distanceToMove;
break;
case SaveFileSeekOrigin::End:
file->currentFilePointer =
file->data.startOffset + file->getFileSize() + distanceToMove;
break;
case SaveFileSeekOrigin::Begin:
default:
file->currentFilePointer = file->data.startOffset + distanceToMove;
break;
}
ReleaseSaveAccess();
}
// If this file needs to grow, move the data after along
void ConsoleSaveFileOriginal::PrepareForWrite(
FileEntry* file, unsigned int nNumberOfBytesToWrite) {
int bytesToGrowBy = ((file->currentFilePointer - file->data.startOffset) +
nNumberOfBytesToWrite) -
file->getFileSize();
if (bytesToGrowBy <= 0) return;
// 4J Stu - Not forcing a minimum size, it is up to the caller to write data
// in sensible amounts This lets us keep some of the smaller files small
// if( bytesToGrowBy < 1024 )
// bytesToGrowBy = 1024;
// Move all the data beyond us
MoveDataBeyond(file, bytesToGrowBy);
// Update our length
if (file->data.length < 0) file->data.length = 0;
file->data.length += bytesToGrowBy;
// Write the header with the updated data
finalizeWrite();
}
bool ConsoleSaveFileOriginal::writeFile(FileEntry* file, const void* lpBuffer,
unsigned int nNumberOfBytesToWrite,
unsigned int* lpNumberOfBytesWritten) {
assert(pvSaveMem != nullptr);
if (pvSaveMem == nullptr) {
return false;
}
LockSaveAccess();
PrepareForWrite(file, nNumberOfBytesToWrite);
char* writeStartOffset = (char*)pvSaveMem + file->currentFilePointer;
// printf("Write: pvSaveMem = %0xd, currentFilePointer = %d,
// writeStartOffset = %0xd\n", pvSaveMem, file->currentFilePointer,
// writeStartOffset);
memcpy((void*)writeStartOffset, lpBuffer, nNumberOfBytesToWrite);
*lpNumberOfBytesWritten = nNumberOfBytesToWrite;
if (file->data.length < 0) file->data.length = 0;
file->currentFilePointer += *lpNumberOfBytesWritten;
// wprintf(L"Wrote %d bytes to %s, new file pointer is %I64d\n",
// *lpNumberOfBytesWritten, file->data.filename, file->currentFilePointer);
file->updateLastModifiedTime();
ReleaseSaveAccess();
return true;
}
bool ConsoleSaveFileOriginal::zeroFile(FileEntry* file,
unsigned int nNumberOfBytesToWrite,
unsigned int* lpNumberOfBytesWritten) {
assert(pvSaveMem != nullptr);
if (pvSaveMem == nullptr) {
return false;
}
LockSaveAccess();
PrepareForWrite(file, nNumberOfBytesToWrite);
char* writeStartOffset = (char*)pvSaveMem + file->currentFilePointer;
// printf("Write: pvSaveMem = %0xd, currentFilePointer = %d,
// writeStartOffset = %0xd\n", pvSaveMem, file->currentFilePointer,
// writeStartOffset);
memset((void*)writeStartOffset, 0, nNumberOfBytesToWrite);
*lpNumberOfBytesWritten = nNumberOfBytesToWrite;
if (file->data.length < 0) file->data.length = 0;
file->currentFilePointer += *lpNumberOfBytesWritten;
// wprintf(L"Wrote %d bytes to %s, new file pointer is %I64d\n",
// *lpNumberOfBytesWritten, file->data.filename, file->currentFilePointer);
file->updateLastModifiedTime();
ReleaseSaveAccess();
return true;
}
bool ConsoleSaveFileOriginal::readFile(FileEntry* file, void* lpBuffer,
unsigned int nNumberOfBytesToRead,
unsigned int* lpNumberOfBytesRead) {
unsigned int actualBytesToRead;
assert(pvSaveMem != nullptr);
if (pvSaveMem == nullptr) {
return false;
}
LockSaveAccess();
char* readStartOffset = (char*)pvSaveMem + file->currentFilePointer;
// printf("Read: pvSaveMem = %0xd, currentFilePointer = %d, readStartOffset
// = %0xd\n", pvSaveMem, file->currentFilePointer, readStartOffset);
assert(nNumberOfBytesToRead <= file->getFileSize());
actualBytesToRead = nNumberOfBytesToRead;
if (file->currentFilePointer + nNumberOfBytesToRead >
file->data.startOffset + file->data.length) {
actualBytesToRead = (file->data.startOffset + file->data.length) -
file->currentFilePointer;
}
memcpy(lpBuffer, readStartOffset, actualBytesToRead);
*lpNumberOfBytesRead = actualBytesToRead;
file->currentFilePointer += *lpNumberOfBytesRead;
// wprintf(L"Read %d bytes from %s, new file pointer is %I64d\n",
// *lpNumberOfBytesRead, file->data.filename, file->currentFilePointer);
ReleaseSaveAccess();
return true;
}
bool ConsoleSaveFileOriginal::closeHandle(FileEntry* file) {
LockSaveAccess();
finalizeWrite();
ReleaseSaveAccess();
return true;
}
void ConsoleSaveFileOriginal::finalizeWrite() {
LockSaveAccess();
header.WriteHeader(pvSaveMem);
ReleaseSaveAccess();
}
void ConsoleSaveFileOriginal::MoveDataBeyond(
FileEntry* file, unsigned int nNumberOfBytesToWrite) {
unsigned int numberOfBytesRead = 0;
unsigned int numberOfBytesWritten = 0;
const unsigned int bufferSize = 4096;
unsigned int amountToRead = bufferSize;
// assert( nNumberOfBytesToWrite <= bufferSize );
static std::uint8_t buffer1[bufferSize];
static std::uint8_t buffer2[bufferSize];
unsigned int buffer1Size = 0;
unsigned int buffer2Size = 0;
// Only ReAlloc if we need to (we might already have enough) and align to
// 512 byte boundaries
unsigned int currentHeapSize = pagesCommitted * CSF_PAGE_SIZE;
unsigned int desiredSize = header.GetFileSize() + nNumberOfBytesToWrite;
if (desiredSize > currentHeapSize) {
unsigned int pagesRequired =
(desiredSize + (CSF_PAGE_SIZE - 1)) / CSF_PAGE_SIZE;
void* pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE,
COMMIT_ALLOCATION, PAGE_READWRITE);
if (pvRet == nullptr) {
// Out of physical memory
__debugbreak();
}
pagesCommitted = pagesRequired;
}
// This is the start of where we want the space to be, and the start of the
// data that we need to move
char* spaceStartOffset =
(char*)pvSaveMem + file->data.startOffset + file->getFileSize();
// This is the end of where we want the space to be
char* spaceEndOffset = spaceStartOffset + nNumberOfBytesToWrite;
// This is the current end of the data that we want to move
char* beginEndOfDataOffset = (char*)pvSaveMem + header.GetStartOfNextData();
// This is where the end of the data is going to be
char* finishEndOfDataOffset = beginEndOfDataOffset + nNumberOfBytesToWrite;
// This is where we are going to read from (with the amount we want to read
// subtracted before we read)
char* readStartOffset = beginEndOfDataOffset;
// This is where we can safely write to (with the amount we want write
// subtracted before we write)
char* writeStartOffset = finishEndOfDataOffset;
// printf("\n******* MOVEDATABEYOND *******\n");
// printf("Space start: %d, space end: %d\n", spaceStartOffset - (char
// *)pvSaveMem, spaceEndOffset - (char *)pvSaveMem); printf("Current end of
// data: %d, new end of data: %d\n", beginEndOfDataOffset - (char
// *)pvSaveMem, finishEndOfDataOffset - (char *)pvSaveMem);
// Optimisation for things that are being moved in whole region file sector
// (4K chunks). We could generalise this a bit more but seems safest at the
// moment to identify this particular type of move and code explicitly for
// this situation
if ((nNumberOfBytesToWrite & 4095) == 0) {
if (nNumberOfBytesToWrite > 0) {
// Get addresses for start & end of the region we are copying from
// as uintptr_t, for easier maths
uintptr_t uiFromStart = (uintptr_t)spaceStartOffset;
uintptr_t uiFromEnd = (uintptr_t)beginEndOfDataOffset;
// Round both of these values to get 4096 byte chunks that we will
// need to at least partially move
uintptr_t uiFromStartChunk = uiFromStart & ~((uintptr_t)4095);
uintptr_t uiFromEndChunk = (uiFromEnd - 1) & ~((uintptr_t)4095);
// Loop through all the affected source 4096 chunks, going backwards
// so we don't overwrite anything we'll need in the future
for (uintptr_t uiCurrentChunk = uiFromEndChunk;
uiCurrentChunk >= uiFromStartChunk; uiCurrentChunk -= 4096) {
// Establish chunk we'll need to copy
uintptr_t uiCopyStart = uiCurrentChunk;
uintptr_t uiCopyEnd = uiCurrentChunk + 4096;
// Clamp chunk to the bounds of the full region we are trying to
// copy
if (uiCopyStart < uiFromStart) {
// Needs to be clampged against the start of our region
uiCopyStart = uiFromStart;
}
if (uiCopyEnd > uiFromEnd) {
// Needs to be clamped to the end of our region
uiCopyEnd = uiFromEnd;
}
XMemCpy((void*)(uiCopyStart + nNumberOfBytesToWrite),
(void*)uiCopyStart, uiCopyEnd - uiCopyStart);
}
}
} else {
while (true) {
// Copy buffer 1 to buffer 2
memcpy(buffer2, buffer1, buffer1Size);
buffer2Size = buffer1Size;
// Fill buffer 1 from file
if ((readStartOffset - bufferSize) < spaceStartOffset) {
amountToRead = static_cast<unsigned int>(readStartOffset -
spaceStartOffset);
} else {
amountToRead = bufferSize;
}
// Push the read point back by the amount of bytes that we are going
// to read
readStartOffset -= amountToRead;
// printf("About to read %u from %d\n", amountToRead,
// readStartOffset - (char *)pvSaveMem );
memcpy(buffer1, readStartOffset, amountToRead);
numberOfBytesRead = amountToRead;
buffer1Size = amountToRead;
// Move back the write pointer by the amount of bytes we are going
// to write
writeStartOffset -= buffer2Size;
// Write buffer 2 to file
if ((writeStartOffset + buffer2Size) <= finishEndOfDataOffset) {
// printf("About to write %u to %d\n", buffer2Size,
// writeStartOffset - (char *)pvSaveMem );
memcpy((void*)writeStartOffset, buffer2, buffer2Size);
numberOfBytesWritten = buffer2Size;
} else {
assert((writeStartOffset + buffer2Size) <=
finishEndOfDataOffset);
numberOfBytesWritten = 0;
}
if (numberOfBytesRead == 0) {
// printf("\n************** MOVE COMPLETED ***************
// \n\n");
assert(writeStartOffset == spaceEndOffset);
break;
}
}
}
header.AdjustStartOffsets(file, nNumberOfBytesToWrite);
}
bool ConsoleSaveFileOriginal::doesFileExist(ConsoleSavePath file) {
LockSaveAccess();
bool exists = header.fileExists(file.getName());
ReleaseSaveAccess();
return exists;
}
void ConsoleSaveFileOriginal::Flush(bool autosave, bool updateThumbnail) {
LockSaveAccess();
finalizeWrite();
float fElapsedTime = 0.0f;
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
// size
unsigned int compLength = fileSize + 8;
// 4J Stu - Added TU-1 interim
// Attempt to allocate the required memory
// We do not own this, it belongs to the StorageManager
std::uint8_t* compData =
(std::uint8_t*)StorageManager.AllocateSaveData(compLength);
// If we failed to allocate then compData will be nullptr
// Pre-calculate the compressed data size so that we can attempt to allocate
// a smaller buffer
if (compData == nullptr) {
// Length should be 0 here so that the compression call knows that we
// want to know the length back
compLength = 0;
// Pre-calculate the buffer size required for the compressed data
PIXBeginNamedEvent(0, "Pre-calc save compression");
// Save the start time
const auto startTime = std::chrono::steady_clock::now();
Compression::getCompression()->Compress(nullptr, &compLength, pvSaveMem,
fileSize);
fElapsedTime = std::chrono::duration<float>(
std::chrono::steady_clock::now() - startTime)
.count();
app.DebugPrintf("Check buffer size: Elapsed time %f\n", fElapsedTime);
PIXEndNamedEvent();
// We add 4 bytes to the start so that we can signal compressed data
// And another 4 bytes to store the decompressed data size
compLength = compLength + 8;
// Attempt to allocate the required memory
compData = (std::uint8_t*)StorageManager.AllocateSaveData(compLength);
}
if (compData != nullptr) {
// Re-compress all save data before we save it to disk
PIXBeginNamedEvent(0, "Actual save compression");
// Save the start time
const auto startTime = std::chrono::steady_clock::now();
Compression::getCompression()->Compress(compData + 8, &compLength,
pvSaveMem, fileSize);
fElapsedTime = std::chrono::duration<float>(
std::chrono::steady_clock::now() - startTime)
.count();
app.DebugPrintf("Compress: Elapsed time %f\n", fElapsedTime);
PIXEndNamedEvent();
std::fill_n(compData, 8, std::uint8_t{0});
int saveVer = 0;
memcpy(compData, &saveVer, sizeof(int));
memcpy(compData + 4, &fileSize, sizeof(int));
app.DebugPrintf("Save data compressed from %d to %d\n", fileSize,
compLength);
std::uint8_t* pbThumbnailData = nullptr;
unsigned int dwThumbnailDataSize = 0;
std::uint8_t* pbDataSaveImage = nullptr;
unsigned int dwDataSizeSaveImage = 0;
#ifdef _WINDOWS64
app.GetSaveThumbnail(&pbThumbnailData, &dwThumbnailDataSize,
&pbDataSaveImage, &dwDataSizeSaveImage);
#endif
std::uint8_t bTextMetadata[88] = {};
int64_t seed = 0;
bool hasSeed = false;
if (MinecraftServer::getInstance() != nullptr &&
MinecraftServer::getInstance()->levels[0] != nullptr) {
seed = MinecraftServer::getInstance()
->levels[0]
->getLevelData()
->getSeed();
hasSeed = true;
}
int iTextMetadataBytes = app.CreateImageTextData(
bTextMetadata, seed, hasSeed,
app.GetGameHostOption(eGameHostOption_All),
Minecraft::GetInstance()->getCurrentTexturePackId());
int32_t saveOrCheckpointId = 0;
bool validSave =
StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId);
TelemetryManager->RecordLevelSaveOrCheckpoint(
ProfileManager.GetPrimaryPad(), saveOrCheckpointId, compLength + 8);
#ifdef _WINDOWS64
// set the icon and save image
StorageManager.SetSaveImages(pbThumbnailData, dwThumbnailDataSize,
pbDataSaveImage, dwDataSizeSaveImage,
bTextMetadata, iTextMetadataBytes);
app.DebugPrintf("Save thumbnail size %d\n", dwThumbnailDataSize);
// save the data
StorageManager.SaveSaveData(
&ConsoleSaveFileOriginal::SaveSaveDataCallback, this);
#ifndef _CONTENT_PACKAGE
if (app.DebugSettingsOn()) {
if (app.GetWriteSavesToFolderEnabled()) {
DebugFlushToFile(compData, compLength + 8);
}
}
#endif
ReleaseSaveAccess();
#else
ReleaseSaveAccess();
#endif
} else {
// We have failed to allocate the memory required to save this file. Now
// what?
ReleaseSaveAccess();
}
}
#ifdef _WINDOWS64
int ConsoleSaveFileOriginal::SaveSaveDataCallback(void* lpParam, bool bRes) {
ConsoleSaveFile* pClass = (ConsoleSaveFile*)lpParam;
return 0;
}
#endif
#ifndef _CONTENT_PACKAGE
void ConsoleSaveFileOriginal::DebugFlushToFile(
void* compressedData /*= nullptr*/,
unsigned int compressedDataSize /*= 0*/) {
LockSaveAccess();
finalizeWrite();
unsigned int fileSize = header.GetFileSize();
unsigned int numberOfBytesWritten = 0;
File targetFileDir(L"Saves");
if (!targetFileDir.exists()) targetFileDir.mkdir();
wchar_t* fileName = new wchar_t[XCONTENT_MAX_FILENAME_LENGTH + 1];
std::time_t now = std::time(nullptr);
std::tm t = *std::gmtime(&now);
// 14 chars for the digits
// 11 chars for the separators + suffix
// 25 chars total
std::wstring cutFileName = m_fileName;
if (m_fileName.length() > XCONTENT_MAX_FILENAME_LENGTH - 25) {
cutFileName = m_fileName.substr(0, XCONTENT_MAX_FILENAME_LENGTH - 25);
}
swprintf(fileName, XCONTENT_MAX_FILENAME_LENGTH + 1,
L"\\v%04d-%ls%02d.%02d.%02d.%02d.%02d.mcs", VER_PRODUCTBUILD,
cutFileName.c_str(), t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min,
t.tm_sec);
const std::wstring outputPath =
targetFileDir.getPath() + std::wstring(fileName);
bool writeSucceeded = false;
if (compressedData != nullptr && compressedDataSize > 0) {
writeSucceeded = PortableFileIO::WriteBinaryFile(
outputPath, compressedData, compressedDataSize);
numberOfBytesWritten = writeSucceeded ? compressedDataSize : 0;
assert(numberOfBytesWritten == compressedDataSize);
} else {
writeSucceeded =
PortableFileIO::WriteBinaryFile(outputPath, pvSaveMem, fileSize);
numberOfBytesWritten = writeSucceeded ? fileSize : 0;
assert(numberOfBytesWritten == fileSize);
}
delete[] fileName;
ReleaseSaveAccess();
}
#endif
unsigned int ConsoleSaveFileOriginal::getSizeOnDisk() {
return header.GetFileSize();
}
std::wstring ConsoleSaveFileOriginal::getFilename() { return m_fileName; }
std::vector<FileEntry*>* ConsoleSaveFileOriginal::getFilesWithPrefix(
const std::wstring& prefix) {
return header.getFilesWithPrefix(prefix);
}
std::vector<FileEntry*>* ConsoleSaveFileOriginal::getRegionFilesByDimension(
unsigned int dimensionIndex) {
return nullptr;
}
int ConsoleSaveFileOriginal::getSaveVersion() {
return header.getSaveVersion();
}
int ConsoleSaveFileOriginal::getOriginalSaveVersion() {
return header.getOriginalSaveVersion();
}
void ConsoleSaveFileOriginal::LockSaveAccess() { m_lock.lock(); }
void ConsoleSaveFileOriginal::ReleaseSaveAccess() { m_lock.unlock(); }
ESavePlatform ConsoleSaveFileOriginal::getSavePlatform() {
return header.getSavePlatform();
}
bool ConsoleSaveFileOriginal::isSaveEndianDifferent() {
return header.isSaveEndianDifferent();
}
void ConsoleSaveFileOriginal::setLocalPlatform() { header.setLocalPlatform(); }
void ConsoleSaveFileOriginal::setPlatform(ESavePlatform plat) {
header.setPlatform(plat);
}
ByteOrder ConsoleSaveFileOriginal::getSaveEndian() {
return header.getSaveEndian();
}
ByteOrder ConsoleSaveFileOriginal::getLocalEndian() {
return header.getLocalEndian();
}
void ConsoleSaveFileOriginal::setEndian(ByteOrder endian) {
header.setEndian(endian);
}
bool ConsoleSaveFileOriginal::isLocalEndianDifferent(ESavePlatform plat) {
return getLocalEndian() != header.getEndian(plat);
}
void ConsoleSaveFileOriginal::ConvertRegionFile(File sourceFile) {
unsigned int numberOfBytesWritten = 0;
unsigned int numberOfBytesRead = 0;
RegionFile sourceRegionFile(this, &sourceFile);
for (unsigned int x = 0; x < 32; ++x) {
for (unsigned int z = 0; z < 32; ++z) {
DataInputStream* dis =
sourceRegionFile.getChunkDataInputStream(x, z);
if (dis) {
byteArray inData(1024 * 1024);
int read = dis->read(inData);
dis->close();
dis->deleteChildStream();
delete dis;
DataOutputStream* dos =
sourceRegionFile.getChunkDataOutputStream(x, z);
dos->write(inData, 0, read);
dos->close();
dos->deleteChildStream();
delete dos;
delete inData.data;
}
}
}
sourceRegionFile
.writeAllOffsets(); // saves all the endian swapped offsets back out to
// the file (not all of these are written in the
// above processing).
}
void ConsoleSaveFileOriginal::ConvertToLocalPlatform() {
if (getSavePlatform() == SAVE_FILE_PLATFORM_LOCAL) {
// already in the correct format
return;
}
// convert each of the region files to the local platform
std::vector<FileEntry*>* allFilesInSave =
getFilesWithPrefix(std::wstring(L""));
for (auto it = allFilesInSave->begin(); it < allFilesInSave->end(); ++it) {
FileEntry* fe = *it;
std::wstring fName(fe->data.filename);
std::wstring suffix(L".mcr");
if (fName.compare(fName.length() - suffix.length(), suffix.length(),
suffix) == 0) {
app.DebugPrintf("Processing a region file: %ls\n", fName.c_str());
ConvertRegionFile(File(fe->data.filename));
} else {
app.DebugPrintf("%ls is not a region file, ignoring\n",
fName.c_str());
}
}
setLocalPlatform(); // set the platform of this save to the local platform,
// now that it's been coverted
}
void* ConsoleSaveFileOriginal::getWritePointer(FileEntry* file) {
return (char*)pvSaveMem + file->currentFilePointer;
;
}

View File

@@ -0,0 +1,100 @@
#pragma once
#include <mutex>
#include "FileHeader.h"
#include "ConsoleSavePath.h"
#include "ConsoleSaveFile.h"
class ConsoleSaveFileOriginal : public ConsoleSaveFile {
private:
FileHeader header;
std::wstring m_fileName;
// void* hHeap;
static void* pvHeap;
static unsigned int pagesCommitted;
#if defined(_LARGE_WORLDS)
static const unsigned int CSF_PAGE_SIZE = 64 * 1024;
static const unsigned int MAX_PAGE_COUNT =
32 * 1024; // 2GB virtual allocation
#else
static const unsigned int CSF_PAGE_SIZE = 64 * 1024;
static const unsigned int MAX_PAGE_COUNT = 1024;
#endif
void* pvSaveMem;
std::recursive_mutex m_lock;
void PrepareForWrite(FileEntry* file, unsigned int nNumberOfBytesToWrite);
void MoveDataBeyond(FileEntry* file, unsigned int nNumberOfBytesToWrite);
public:
#if defined(_WINDOWS64)
static int SaveSaveDataCallback(void* lpParam, bool bRes);
#endif
ConsoleSaveFileOriginal(const std::wstring& fileName,
void* pvSaveData = nullptr,
unsigned int fileSize = 0,
bool forceCleanSave = false,
ESavePlatform plat = SAVE_FILE_PLATFORM_LOCAL);
virtual ~ConsoleSaveFileOriginal();
// 4J Stu - Initial implementation is intended to have a similar interface
// to the standard Xbox file access functions
virtual FileEntry* createFile(const ConsoleSavePath& fileName);
virtual void deleteFile(FileEntry* file);
virtual void setFilePointer(FileEntry* file, unsigned int distanceToMove,
SaveFileSeekOrigin seekOrigin);
virtual bool writeFile(FileEntry* file, const void* lpBuffer,
unsigned int nNumberOfBytesToWrite,
unsigned int* lpNumberOfBytesWritten);
virtual bool zeroFile(FileEntry* file, unsigned int nNumberOfBytesToWrite,
unsigned int* lpNumberOfBytesWritten);
virtual bool readFile(FileEntry* file, void* lpBuffer,
unsigned int nNumberOfBytesToRead,
unsigned int* lpNumberOfBytesRead);
virtual bool closeHandle(FileEntry* file);
virtual void finalizeWrite();
virtual bool doesFileExist(ConsoleSavePath file);
virtual void Flush(bool autosave, bool updateThumbnail = true);
#if !defined(_CONTENT_PACKAGE)
virtual void DebugFlushToFile(void* compressedData = nullptr,
unsigned int compressedDataSize = 0);
#endif
virtual unsigned int getSizeOnDisk();
virtual std::wstring getFilename();
virtual std::vector<FileEntry*>* getFilesWithPrefix(
const std::wstring& prefix);
virtual std::vector<FileEntry*>* getRegionFilesByDimension(
unsigned int dimensionIndex);
virtual int getSaveVersion();
virtual int getOriginalSaveVersion();
virtual void LockSaveAccess();
virtual void ReleaseSaveAccess();
virtual ESavePlatform getSavePlatform();
virtual bool isSaveEndianDifferent();
virtual void setLocalPlatform();
virtual void setPlatform(ESavePlatform plat);
virtual ByteOrder getSaveEndian();
virtual ByteOrder getLocalEndian();
virtual void setEndian(ByteOrder endian);
virtual bool isLocalEndianDifferent(ESavePlatform plat);
virtual void ConvertRegionFile(File sourceFile);
virtual void ConvertToLocalPlatform();
protected:
virtual void* getWritePointer(FileEntry* file);
};

View File

@@ -0,0 +1,116 @@
#include "../../Header Files/stdafx.h"
#include "../../ConsoleJavaLibs/File.h"
#include "ConsoleSaveFileOutputStream.h"
#include "ConsoleSaveFile.h"
// Creates a file output stream to write to the file represented by the
// specified File object. A new FileDescriptor object is created to represent
// this file connection. First, if there is a security manager, its checkWrite
// method is called with the path represented by the file argument as its
// argument.
//
// If the file exists but is a directory rather than a regular file, does not
// exist but cannot be created, or cannot be opened for any other reason then a
// FileNotFoundException is thrown.
//
// Parameters:
// file - the file to be opened for writing.
ConsoleSaveFileOutputStream::ConsoleSaveFileOutputStream(
ConsoleSaveFile* saveFile, const ConsoleSavePath& file) {
m_saveFile = saveFile;
m_file = m_saveFile->createFile(file);
m_saveFile->setFilePointer(m_file, 0, SaveFileSeekOrigin::Begin);
}
ConsoleSaveFileOutputStream::ConsoleSaveFileOutputStream(
ConsoleSaveFile* saveFile, FileEntry* file) {
m_saveFile = saveFile;
m_file = file;
m_saveFile->setFilePointer(m_file, 0, SaveFileSeekOrigin::Begin);
}
// Writes the specified byte to this file output stream. Implements the write
// method of OutputStream. Parameters: b - the byte to be written.
void ConsoleSaveFileOutputStream::write(unsigned int b) {
unsigned int numberOfBytesWritten;
std::uint8_t value = (std::uint8_t)b;
bool result =
m_saveFile->writeFile(m_file,
&value, // data buffer
1, // number of bytes to write
&numberOfBytesWritten // number of bytes written
);
if (!result) {
// TODO 4J Stu - Some kind of error handling
} else if (numberOfBytesWritten == 0) {
// File pointer is past the end of the file
}
}
// Writes b.length bytes from the specified byte array to this file output
// stream. Parameters: b - the data.
void ConsoleSaveFileOutputStream::write(byteArray b) {
unsigned int numberOfBytesWritten;
bool result =
m_saveFile->writeFile(m_file,
&b.data, // data buffer
b.length, // number of bytes to write
&numberOfBytesWritten // number of bytes written
);
if (!result) {
// TODO 4J Stu - Some kind of error handling
} else if (numberOfBytesWritten == 0 || numberOfBytesWritten != b.length) {
// File pointer is past the end of the file
}
}
// Writes len bytes from the specified byte array starting at offset off to this
// file output stream. Parameters: b - the data. off - the start offset in the
// data. len - the number of bytes to write.
void ConsoleSaveFileOutputStream::write(byteArray b, unsigned int offset,
unsigned int length) {
// 4J Stu - We don't want to write any more than the array buffer holds
assert(length <= (b.length - offset));
unsigned int numberOfBytesWritten;
bool result =
m_saveFile->writeFile(m_file,
&b[offset], // data buffer
length, // number of bytes to write
&numberOfBytesWritten // number of bytes written
);
if (!result) {
// TODO 4J Stu - Some kind of error handling
} else if (numberOfBytesWritten == 0 || numberOfBytesWritten != length) {
// File pointer is past the end of the file
}
}
//
// Closes this file output stream and releases any system resources associated
// with this stream. This file output stream may no longer be used for writing
// bytes. If this stream has an associated channel then the channel is closed as
// well.
void ConsoleSaveFileOutputStream::close() {
if (m_saveFile != nullptr) {
bool result = m_saveFile->closeHandle(m_file);
if (!result) {
// TODO 4J Stu - Some kind of error handling
}
// Stop the dtor from trying to close it again
m_saveFile = nullptr;
}
}

View File

@@ -0,0 +1,26 @@
#pragma once
// 4J Stu - Implements the Java InputStream but rather than writing directly to
// disc it writes through the save file
#include "../../ConsoleJavaLibs/InputOutputStream/OutputStream.h"
#include "ConsoleSavePath.h"
class ConsoleSaveFile;
class FileEntry;
class ConsoleSaveFileOutputStream : public OutputStream {
public:
ConsoleSaveFileOutputStream(ConsoleSaveFile* saveFile,
const ConsoleSavePath& file);
ConsoleSaveFileOutputStream(ConsoleSaveFile* saveFile, FileEntry* file);
virtual void write(unsigned int b);
virtual void write(byteArray b);
virtual void write(byteArray b, unsigned int offset, unsigned int length);
virtual void close();
virtual void flush() {}
private:
ConsoleSaveFile* m_saveFile;
FileEntry* m_file;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,166 @@
#pragma once
#include <mutex>
#include "FileHeader.h"
#include "ConsoleSavePath.h"
#include "ConsoleSaveFile.h"
class ProgressRenderer;
class ConsoleSaveFileSplit : public ConsoleSaveFile {
private:
FileHeader header;
static const int WRITE_BANDWIDTH_BYTESPERSECOND =
1048576; // Average bytes per second we will cap to when writing region
// files during the tick() method
static const int WRITE_BANDWIDTH_MEASUREMENT_PERIOD_SECONDS =
10; // Time period over which the bytes per second average is
// calculated
static const int WRITE_TICK_RATE_MS =
500; // Time between attempts to work out which regions we should write
// during the tick
static const int WRITE_MAX_WRITE_PER_TICK =
WRITE_BANDWIDTH_BYTESPERSECOND; // Maximum number of bytes we can add
// in a single tick
class WriteHistory {
public:
std::int64_t writeTime;
unsigned int writeSize;
};
class DirtyRegionFile {
public:
std::int64_t lastWritten;
unsigned int fileRef;
bool operator<(const DirtyRegionFile& rhs) const {
return lastWritten < rhs.lastWritten;
}
};
class RegionFileReference {
public:
RegionFileReference(int index, unsigned int regionIndex,
unsigned int length = 0,
unsigned char* data = nullptr);
~RegionFileReference();
void Compress(); // Compress from data to dataCompressed
void Decompress(); // Decompress from dataCompressed -> data
unsigned int GetCompressedSize(); // Gets byte size for what this
// region will compress to
void ReleaseCompressed(); // Release dataCompressed
FileEntry* fileEntry;
unsigned char* data;
unsigned char* dataCompressed;
unsigned int dataCompressedSize;
int index;
bool dirty;
std::int64_t lastWritten;
};
std::unordered_map<unsigned int, RegionFileReference*> regionFiles;
std::vector<WriteHistory> writeHistory;
std::int64_t m_lastTickTime;
FileEntry* GetRegionFileEntry(unsigned int regionIndex);
std::wstring m_fileName;
bool m_autosave;
// void* hHeap;
static void* pvHeap;
static unsigned int pagesCommitted;
#if defined(_LARGE_WORLDS)
static const unsigned int CSF_PAGE_SIZE = 64 * 1024;
static const unsigned int MAX_PAGE_COUNT =
32 * 1024; // 2GB virtual allocation
#else
static const unsigned int CSF_PAGE_SIZE = 64 * 1024;
static const unsigned int MAX_PAGE_COUNT = 1024;
#endif
void* pvSaveMem;
std::recursive_mutex m_lock;
void PrepareForWrite(FileEntry* file, unsigned int nNumberOfBytesToWrite);
void MoveDataBeyond(FileEntry* file, unsigned int nNumberOfBytesToWrite);
bool GetNumericIdentifierFromName(const std::wstring& fileName,
unsigned int* idOut);
std::wstring GetNameFromNumericIdentifier(unsigned int idIn);
void processSubfilesForWrite();
void processSubfilesAfterWrite();
public:
static int SaveSaveDataCallback(void* lpParam, bool bRes);
static int SaveRegionFilesCallback(void* lpParam, bool bRes);
private:
void _init(const std::wstring& fileName, void* pvSaveData,
unsigned int fileSize, ESavePlatform plat);
public:
ConsoleSaveFileSplit(const std::wstring& fileName,
void* pvSaveData = nullptr, unsigned int fileSize = 0,
bool forceCleanSave = false,
ESavePlatform plat = SAVE_FILE_PLATFORM_LOCAL);
ConsoleSaveFileSplit(ConsoleSaveFile* sourceSave,
bool alreadySmallRegions = true,
ProgressListener* progress = nullptr);
virtual ~ConsoleSaveFileSplit();
// 4J Stu - Initial implementation is intended to have a similar interface
// to the standard Xbox file access functions
virtual FileEntry* createFile(const ConsoleSavePath& fileName);
virtual void deleteFile(FileEntry* file);
virtual void setFilePointer(FileEntry* file, unsigned int distanceToMove,
SaveFileSeekOrigin seekOrigin);
virtual bool writeFile(FileEntry* file, const void* lpBuffer,
unsigned int nNumberOfBytesToWrite,
unsigned int* lpNumberOfBytesWritten);
virtual bool zeroFile(FileEntry* file, unsigned int nNumberOfBytesToWrite,
unsigned int* lpNumberOfBytesWritten);
virtual bool readFile(FileEntry* file, void* lpBuffer,
unsigned int nNumberOfBytesToRead,
unsigned int* lpNumberOfBytesRead);
virtual bool closeHandle(FileEntry* file);
virtual void finalizeWrite();
virtual void tick();
virtual bool doesFileExist(ConsoleSavePath file);
virtual void Flush(bool autosave, bool updateThumbnail = true);
#if !defined(_CONTENT_PACKAGE)
virtual void DebugFlushToFile(void* compressedData = nullptr,
unsigned int compressedDataSize = 0);
#endif
virtual unsigned int getSizeOnDisk();
virtual std::wstring getFilename();
virtual std::vector<FileEntry*>* getFilesWithPrefix(
const std::wstring& prefix);
virtual std::vector<FileEntry*>* getRegionFilesByDimension(
unsigned int dimensionIndex);
virtual int getSaveVersion();
virtual int getOriginalSaveVersion();
virtual void LockSaveAccess();
virtual void ReleaseSaveAccess();
virtual ESavePlatform getSavePlatform();
virtual bool isSaveEndianDifferent();
virtual void setLocalPlatform();
virtual void setPlatform(ESavePlatform plat);
virtual ByteOrder getSaveEndian();
virtual ByteOrder getLocalEndian();
virtual void setEndian(ByteOrder endian);
virtual void ConvertRegionFile(File sourceFile);
virtual void ConvertToLocalPlatform();
};

View File

@@ -0,0 +1,13 @@
#pragma once
class ConsoleSavePath {
private:
std::wstring path;
public:
ConsoleSavePath(const std::wstring& newPath) { path = newPath; }
std::wstring getName() const { return path; }
std::wstring operator+(std::wstring& b) { return path + b; }
};

View File

@@ -0,0 +1,364 @@
#include "../../Header Files/stdafx.h"
#include "FileHeader.h"
// #define _DEBUG_FILE_HEADER
extern CConsoleMinecraftApp app;
FileHeader::FileHeader() {
lastFile = nullptr;
m_saveVersion = 0;
// New saves should have an original version set to the latest version. This
// will be overridden when we load a save
m_originalSaveVersion = SAVE_FILE_VERSION_NUMBER;
m_savePlatform = SAVE_FILE_PLATFORM_LOCAL;
m_saveEndian = m_localEndian;
}
FileHeader::~FileHeader() {
for (unsigned int i = 0; i < fileTable.size(); ++i) {
delete fileTable[i];
}
}
FileEntry* FileHeader::AddFile(const std::wstring& name,
unsigned int length /* = 0 */) {
assert(name.length() < 64);
wchar_t filename[64];
memset(&filename, 0, sizeof(wchar_t) * 64);
memcpy(&filename, name.c_str(),
std::min(sizeof(wchar_t) * 64, sizeof(wchar_t) * name.length()));
// Would a map be more efficient? Our file tables probably won't be very big
// so better to avoid hashing all the time? Does the file exist?
for (unsigned int i = 0; i < fileTable.size(); ++i) {
if (wcscmp(fileTable[i]->data.filename, filename) == 0) {
// If so, return it
return fileTable[i];
}
}
// Else, add it to our file table
fileTable.push_back(new FileEntry(filename, length, GetStartOfNextData()));
lastFile = fileTable[fileTable.size() - 1];
return lastFile;
}
void FileHeader::RemoveFile(FileEntry* file) {
if (file == nullptr) return;
AdjustStartOffsets(file, file->getFileSize(), true);
auto it = find(fileTable.begin(), fileTable.end(), file);
if (it < fileTable.end()) {
fileTable.erase(it);
}
#if !defined(_CONTENT_PACKAGE)
wprintf(L"Removed file %ls\n", file->data.filename);
#endif
delete file;
}
void FileHeader::WriteHeader(void* saveMem) {
unsigned int headerOffset = GetStartOfNextData();
// 4J Changed for save version 2 to be the number of files rather than the
// size in bytes
unsigned int headerSize = (int)(fileTable.size());
// uint32_t numberOfBytesWritten = 0;
// Write the offset of the header
// assert(numberOfBytesWritten == 4);
int* begin = (int*)saveMem;
*begin = headerOffset;
// Write the size of the header
// assert(numberOfBytesWritten == 4);
*(begin + 1) = headerSize;
short* versions = (short*)(begin + 2);
// Write the original version number
*versions = m_originalSaveVersion;
// Write the version number
short versionNumber = SAVE_FILE_VERSION_NUMBER;
// assert(numberOfBytesWritten == 4);
//*(begin + 2) = versionNumber;
*(versions + 1) = versionNumber;
#if defined(_DEBUG_FILE_HEADER)
app.DebugPrintf(
"Write save file with original version: %d, and current version %d\n",
m_originalSaveVersion, versionNumber);
#endif
char* headerPosition = (char*)saveMem + headerOffset;
#if defined(_DEBUG_FILE_HEADER)
app.DebugPrintf("\n\nWrite file Header: Offset = %d, Size = %d\n",
headerOffset, headerSize);
#endif
// Write the header
for (unsigned int i = 0; i < fileTable.size(); ++i) {
// wprintf(L"File: %ls, Start = %d, Length = %d, End = %d\n",
// fileTable[i]->data.filename, fileTable[i]->data.startOffset,
// fileTable[i]->data.length, fileTable[i]->data.startOffset +
// fileTable[i]->data.length);
memcpy((void*)headerPosition, &fileTable[i]->data,
sizeof(FileEntrySaveData));
// assert(numberOfBytesWritten == sizeof(FileEntrySaveData));
headerPosition += sizeof(FileEntrySaveData);
}
}
void FileHeader::ReadHeader(
void* saveMem, ESavePlatform plat /*= SAVE_FILE_PLATFORM_LOCAL */) {
unsigned int headerOffset;
unsigned int headerSize;
m_savePlatform = plat;
switch (m_savePlatform) {
case SAVE_FILE_PLATFORM_X360:
case SAVE_FILE_PLATFORM_PS3:
m_saveEndian = BIGENDIAN;
break;
case SAVE_FILE_PLATFORM_XBONE:
case SAVE_FILE_PLATFORM_WIN64:
case SAVE_FILE_PLATFORM_PS4:
case SAVE_FILE_PLATFORM_PSVITA:
m_saveEndian = LITTLEENDIAN;
break;
default:
assert(0);
m_savePlatform = SAVE_FILE_PLATFORM_LOCAL;
m_saveEndian = m_localEndian;
break;
}
// Read the offset of the header
// assert(numberOfBytesRead == 4);
int* begin = (int*)saveMem;
headerOffset = *begin;
if (isSaveEndianDifferent()) System::ReverseULONG(&headerOffset);
// Read the size of the header
// assert(numberOfBytesRead == 4);
headerSize = *(begin + 1);
if (isSaveEndianDifferent()) System::ReverseULONG(&headerSize);
short* versions = (short*)(begin + 2);
// Read the original save version number
m_originalSaveVersion = *(versions);
if (isSaveEndianDifferent()) System::ReverseSHORT(&m_originalSaveVersion);
// Read the save version number
// m_saveVersion = *(begin + 2);
m_saveVersion = *(versions + 1);
if (isSaveEndianDifferent()) System::ReverseSHORT(&m_saveVersion);
#if defined(_DEBUG_FILE_HEADER)
app.DebugPrintf(
"Read save file with orignal version: %d, and current version %d\n",
m_originalSaveVersion, m_saveVersion);
app.DebugPrintf("\n\nRead file Header: Offset = %d, Size = %d\n",
headerOffset, headerSize);
#endif
char* headerPosition = (char*)saveMem + headerOffset;
switch (m_saveVersion) {
// case SAVE_FILE_VERSION_NUMBER:
// case 8: // 4J Stu - SAVE_FILE_VERSION_NUMBER 2,3,4,5,6,7,8 are the
// same, but: : Bumped
// it to 3 in TU5 to force older builds (ie 0062) to
// generate a new world when trying to load new saves
// : Bumped it to 4 in TU9 to delete versions of The End that were
// generated in builds prior to TU9
// : Bumped it to 5 in TU9 to update the map data that was only using 1
// bit to determine dimension
// : Bumped it to 6 for PS3 v1 to update map data mappings to use larger
// PlayerUID : Bumped
// it to 7 for Durango v1 to update map data mappings to use string
// based PlayerUID
// : Bumped it to 8 for Durango v1 when to save the chunks in a
// different compressed format
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:
case SAVE_FILE_VERSION_MOVED_STRONGHOLD:
case SAVE_FILE_VERSION_NEW_END:
case SAVE_FILE_VERSION_POST_LAUNCH:
case SAVE_FILE_VERSION_LAUNCH: {
// Changes for save file version 2:
// headerSize is now a count of elements rather than a count of
// bytes The FileEntrySaveData struct has a lastModifiedTime member
// Read the header
FileEntrySaveData* fesdHeaderPosition =
(FileEntrySaveData*)headerPosition;
for (unsigned int i = 0; i < headerSize; ++i) {
FileEntry* entry = new FileEntry();
// assert(numberOfBytesRead == sizeof(FileEntrySaveData));
memcpy(&entry->data, fesdHeaderPosition,
sizeof(FileEntrySaveData));
if (isSaveEndianDifferent()) {
// Reverse bytes
// System::ReverseWCHARA(entry->data.filename,64);
System::ReverseULONG(&entry->data.length);
System::ReverseULONG(&entry->data.startOffset);
System::ReverseULONGLONG(&entry->data.lastModifiedTime);
}
entry->currentFilePointer = entry->data.startOffset;
lastFile = entry;
fileTable.push_back(entry);
#if defined(_DEBUG_FILE_HEADER)
app.DebugPrintf(
"File: %ls, Start = %d, Length = %d, End = %d, Timestamp = "
"%lld\n",
entry->data.filename, entry->data.startOffset,
entry->data.length,
entry->data.startOffset + entry->data.length,
entry->data.lastModifiedTime);
#endif
fesdHeaderPosition++;
}
} break;
// Legacy save versions, with updated code to convert the
// FileEntrySaveData to the latest version 4J Stu - At time of writing,
// the tutorial save is V1 so need to keep this for compatibility
case SAVE_FILE_VERSION_PRE_LAUNCH: {
// Read the header
// We can then make headerPosition a FileEntrySaveData pointer and
// just increment by one up to the number
unsigned int i = 0;
while (i < headerSize) {
FileEntry* entry = new FileEntry();
// assert(numberOfBytesRead == sizeof(FileEntrySaveData));
memcpy(&entry->data, headerPosition,
sizeof(FileEntrySaveDataV1));
entry->currentFilePointer = entry->data.startOffset;
lastFile = entry;
fileTable.push_back(entry);
#if defined(_DEBUG_FILE_HEADER)
app.DebugPrintf(
"File: %ls, Start = %d, Length = %d, End = %d\n",
entry->data.filename, entry->data.startOffset,
entry->data.length,
entry->data.startOffset + entry->data.length);
#endif
i += sizeof(FileEntrySaveDataV1);
headerPosition += sizeof(FileEntrySaveDataV1);
}
} break;
default:
#if !defined(_CONTENT_PACKAGE)
app.DebugPrintf("********** Invalid save version %d\n",
m_saveVersion);
__debugbreak();
#endif
break;
}
}
unsigned int FileHeader::GetStartOfNextData() {
// The first 4 bytes is the location of the header (the header itself is at
// the end of the file) Then 4 bytes for the size of the header Then 2 bytes
// for the version number at which this save was first generated Then 2
// bytes for the version number that the save should now be at
unsigned int totalBytesSoFar = SAVE_FILE_HEADER_SIZE;
for (unsigned int i = 0; i < fileTable.size(); ++i) {
if (fileTable[i]->getFileSize() > 0)
totalBytesSoFar += fileTable[i]->getFileSize();
}
return totalBytesSoFar;
}
unsigned int FileHeader::GetFileSize() {
return GetStartOfNextData() +
(sizeof(FileEntrySaveData) * (unsigned int)fileTable.size());
}
void FileHeader::AdjustStartOffsets(FileEntry* file,
unsigned int nNumberOfBytesToWrite,
bool subtract /*= false*/) {
bool found = false;
for (unsigned int i = 0; i < fileTable.size(); ++i) {
if (found == true) {
if (subtract) {
fileTable[i]->data.startOffset -= nNumberOfBytesToWrite;
fileTable[i]->currentFilePointer -= nNumberOfBytesToWrite;
} else {
fileTable[i]->data.startOffset += nNumberOfBytesToWrite;
fileTable[i]->currentFilePointer += nNumberOfBytesToWrite;
}
} else if (fileTable[i] == file) {
found = true;
}
}
}
bool FileHeader::fileExists(const std::wstring& name) {
for (unsigned int i = 0; i < fileTable.size(); ++i) {
if (wcscmp(fileTable[i]->data.filename, name.c_str()) == 0) {
// If so, return it
return true;
}
}
return false;
}
std::vector<FileEntry*>* FileHeader::getFilesWithPrefix(
const std::wstring& prefix) {
std::vector<FileEntry*>* files = nullptr;
for (unsigned int i = 0; i < fileTable.size(); ++i) {
if (wcsncmp(fileTable[i]->data.filename, prefix.c_str(),
prefix.size()) == 0) {
if (files == nullptr) {
files = new std::vector<FileEntry*>();
}
files->push_back(fileTable[i]);
}
}
return files;
}
ByteOrder FileHeader::getEndian(ESavePlatform plat) {
ByteOrder platEndian;
switch (plat) {
case SAVE_FILE_PLATFORM_X360:
case SAVE_FILE_PLATFORM_PS3:
return BIGENDIAN;
break;
case SAVE_FILE_PLATFORM_NONE:
case SAVE_FILE_PLATFORM_XBONE:
case SAVE_FILE_PLATFORM_PS4:
case SAVE_FILE_PLATFORM_PSVITA:
case SAVE_FILE_PLATFORM_WIN64:
return LITTLEENDIAN;
break;
default:
assert(0);
break;
}
return LITTLEENDIAN;
}

View File

@@ -0,0 +1,206 @@
#pragma once
#include "../../ConsoleJavaLibs/System.h"
// The first 4 bytes is the location of the header (the header itself is at the
// end of the file) Then 4 bytes for the size of the header Then 2 bytes for the
// version number at which this save was first generated Then 2 bytes for the
// version number that the save should now be at ( the rest of the header is
// actually a footer )
#define SAVE_FILE_HEADER_SIZE 12
enum ESaveVersions {
// Pre-release version
SAVE_FILE_VERSION_PRE_LAUNCH = 1,
// This is the version at which we launched the Xbox360 version
SAVE_FILE_VERSION_LAUNCH = 2,
// This is the version at which we had made changes that broke older saves
SAVE_FILE_VERSION_POST_LAUNCH = 3,
// This is the version at which we introduced the End, and any saves older
// than this will have their End data deleted
SAVE_FILE_VERSION_NEW_END = 4,
// This is the version at which we change the stronghold generation, and any
// saves older than this should should the original version
SAVE_FILE_VERSION_MOVED_STRONGHOLD = 5,
// This is the version at which we changed the playeruid format for PS3
SAVE_FILE_VERSION_CHANGE_MAP_DATA_MAPPING_SIZE = 6,
// This is the version at which we changed the playeruid format for Xbox One
SAVE_FILE_VERSION_DURANGO_CHANGE_MAP_DATA_MAPPING_SIZE = 7,
// This is the version at which we changed the chunk format to directly save
// the compressed storage formats
SAVE_FILE_VERSION_COMPRESSED_CHUNK_STORAGE,
// This is the version at which we added inhabited time to chunk (1.6.4)
SAVE_FILE_VERSION_CHUNK_INHABITED_TIME,
// 4J Stu - If you add a new version here, the save conversion tool will
// also need updated to be able to read this new format
SAVE_FILE_VERSION_NEXT,
};
// This is the version at which we changed the playeruid format for Xbox One
#define SAVE_FILE_VERSION_DURANGO_CHANGE_MAP_DATA_MAPPING_SIZE 7
enum ESavePlatform {
SAVE_FILE_PLATFORM_NONE = MAKE_FOURCC('N', 'O', 'N', 'E'),
SAVE_FILE_PLATFORM_X360 = MAKE_FOURCC('X', '3', '6', '0'),
SAVE_FILE_PLATFORM_XBONE = MAKE_FOURCC('X', 'B', '1', '_'),
SAVE_FILE_PLATFORM_PS3 = MAKE_FOURCC('P', 'S', '3', '_'),
SAVE_FILE_PLATFORM_PS4 = MAKE_FOURCC('P', 'S', '4', '_'),
SAVE_FILE_PLATFORM_PSVITA = MAKE_FOURCC('P', 'S', 'V', '_'),
SAVE_FILE_PLATFORM_WIN64 = MAKE_FOURCC('W', 'I', 'N', '_'),
#if defined(_WINDOWS64)
SAVE_FILE_PLATFORM_LOCAL = SAVE_FILE_PLATFORM_WIN64
#else
// DecalOverdose(HACK + TODO)
SAVE_FILE_PLATFORM_LOCAL = SAVE_FILE_PLATFORM_WIN64
#endif
};
#define SAVE_FILE_VERSION_NUMBER (SAVE_FILE_VERSION_NEXT - 1)
struct FileEntrySaveDataV1 {
public:
wchar_t filename[64]; // 64 * 2B
unsigned int length; // In bytes // 4B
// This is only valid once the save file has been written/loaded at least
// once
unsigned int startOffset; // 4B
};
// It's important that we keep the order and size of the data here to smooth
// updating 4J Stu - As of writing the tutorial level uses a V1 save file
struct FileEntrySaveDataV2 {
public:
wchar_t filename[64]; // 64 * 2B
unsigned int length; // In bytes // 4B
union {
// This is only valid once the save file has been written/loaded at
// least once
unsigned int startOffset; // 4B
// For region files stored via ConsolveSaveFileSplit, these aren't
// stored within the normal save file, identified by not having a name
// (filename[0] is 0). Note: These won't be read or written as part of a
// file header, and should only exist wrapped up in a FileEntry class
unsigned int regionIndex; // 4B
};
int64_t lastModifiedTime; // 8B
};
typedef FileEntrySaveDataV2 FileEntrySaveData;
class FileEntry {
public:
FileEntrySaveData data;
unsigned int currentFilePointer;
FileEntry() { ZeroMemory(&data, sizeof(FileEntrySaveData)); }
FileEntry(wchar_t name[64], unsigned int length, unsigned int startOffset) {
data.length = length;
data.startOffset = startOffset;
memset(&data.filename, 0, sizeof(wchar_t) * 64);
memcpy(&data.filename, name, sizeof(wchar_t) * 64);
data.lastModifiedTime = 0;
currentFilePointer = data.startOffset;
}
unsigned int getFileSize() { return data.length; }
bool isRegionFile() {
return data.filename[0] == 0;
} // When using ConsoleSaveFileSplit only
unsigned int getRegionFileIndex() {
return data.regionIndex;
} // When using ConsoleSaveFileSplit only
void updateLastModifiedTime() {
data.lastModifiedTime = System::currentRealTimeMillis();
}
/*
Comparison function object that returns true if the first argument goes
before the second argument in the specific strict weak ordering it defines,
and false otherwise. Used in a call to std::sort in
DirectoryLevelStorage.cpp
*/
static bool newestFirst(FileEntry* a, FileEntry* b) {
return a->data.lastModifiedTime > b->data.lastModifiedTime;
}
};
// A class the represents the header of the save file
class FileHeader {
friend class ConsoleSaveFileOriginal;
friend class ConsoleSaveFileSplit;
private:
std::vector<FileEntry*> fileTable;
ESavePlatform m_savePlatform;
ByteOrder m_saveEndian;
static const ByteOrder m_localEndian = LITTLEENDIAN;
short m_saveVersion;
short m_originalSaveVersion;
public:
FileEntry* lastFile;
public:
FileHeader();
~FileHeader();
protected:
FileEntry* AddFile(const std::wstring& name, unsigned int length = 0);
void RemoveFile(FileEntry*);
void WriteHeader(void* saveMem);
void ReadHeader(void* saveMem,
ESavePlatform plat = SAVE_FILE_PLATFORM_LOCAL);
unsigned int GetStartOfNextData();
unsigned int GetFileSize();
void AdjustStartOffsets(FileEntry* file, unsigned int nNumberOfBytesToWrite,
bool subtract = false);
bool fileExists(const std::wstring& name);
std::vector<FileEntry*>* getFilesWithPrefix(const std::wstring& prefix);
std::vector<FileEntry*>* getValidPlayerDatFiles();
void setSaveVersion(int version) { m_saveVersion = version; }
int getSaveVersion() { return m_saveVersion; }
void setOriginalSaveVersion(int version) {
m_originalSaveVersion = version;
}
int getOriginalSaveVersion() { return m_originalSaveVersion; }
ESavePlatform getSavePlatform() { return m_savePlatform; }
void setPlatform(ESavePlatform plat) { m_savePlatform = plat; }
bool isSaveEndianDifferent() { return m_saveEndian != m_localEndian; }
void setLocalPlatform() {
m_savePlatform = SAVE_FILE_PLATFORM_LOCAL;
m_saveEndian = m_localEndian;
}
ByteOrder getSaveEndian() { return m_saveEndian; }
static ByteOrder getLocalEndian() { return m_localEndian; }
void setEndian(ByteOrder endian) { m_saveEndian = endian; }
static ByteOrder getEndian(ESavePlatform plat);
bool isLocalEndianDifferent(ESavePlatform plat) {
return m_localEndian != getEndian(plat);
}
};