4jcraft/Minecraft.World/IO/Files/ConsoleSaveFileConverter.cpp
2026-03-13 17:06:56 -05:00

310 lines
11 KiB
C++

#include "../../Platform/stdafx.h"
#include "../../Headers/net.minecraft.world.level.chunk.storage.h"
#include "../../Headers/net.minecraft.world.level.storage.h"
#include "ConsoleSaveFileIO.h"
#include "ConsoleSaveFileConverter.h"
#include "../../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
#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__)
std::vector<FileEntry*>* playerFiles = sourceSave->getValidPlayerDatFiles();
#else
std::vector<FileEntry*>* playerFiles =
sourceSave->getFilesWithPrefix(DirectoryLevelStorage::getPlayerDir());
#endif
if (playerFiles != NULL) {
for (int fileIdx = 0; fileIdx < playerFiles->size(); fileIdx++) {
ConsoleSavePath sourcePlayerDatPath(
playerFiles->at(fileIdx)->data.filename);
#ifdef _XBOX_ONE
// 4J Stu - As the XUIDs on X360 and X1 are different, we don't want
// to transfer these over. However as the first player file should
// be the owner of the save, we can move their data over to the
// current players XUID
if (fileIdx > 0) break;
PlayerUID xuid;
ProfileManager.GetXUID(ProfileManager.GetPrimaryPad(), &xuid,
false);
ConsoleSavePath targetPlayerDatPath(L"players/" + xuid.toString() +
L".dat");
#else
ConsoleSavePath targetPlayerDatPath(
playerFiles->at(fileIdx)->data.filename);
#endif
{
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;
}
#ifdef 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_VAR(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) {
#ifndef _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 {
#ifndef _CONTENT_PACKAGE
wprintf(L"%s is not a region file, ignoring\n",
fe->data.filename);
#endif
}
}
}
#endif
}