#include "stdafx.h" #include "JavaMath.h" #include "BasicTypeContainers.h" #if 0 // 4J - not required anymore #include "Matcher.h" #endif #include "ProgressListener.h" #include "net.minecraft.world.level.chunk.storage.h" #include "net.minecraft.world.level.chunk.h" #include "LevelSummary.h" #include "McRegionLevelStorage.h" #include "File.h" #include "LevelData.h" #include "McRegionLevelStorageSource.h" #include "ConsoleSaveFileIO.h" #if 0 // 4J - not required anymore // These were Pattern class objects, using the c++0x regex class instead const std::tr1::wregex McRegionLevelStorageSource::FolderFilter::chunkFolderPattern = std::tr1::wregex(L"[0-9a-z]|([0-9a-z][0-9a-z])"); const std::tr1::wregex McRegionLevelStorageSource::ChunkFilter::chunkFilePattern = std::tr1::wregex(L"c\\.(-?[0-9a-z]+)\\.(-?[0-9a-z]+)\\.dat"); #endif McRegionLevelStorageSource::McRegionLevelStorageSource(File dir) : DirectoryLevelStorageSource(dir) { } wstring McRegionLevelStorageSource::getName() { return L"Scaevolus' McRegion"; } vector *McRegionLevelStorageSource::getLevelList() { // 4J Stu - We don't need to do directory lookups with the xbox save files vector *levels = new vector; #if 0 vector *subFolders = baseDir.listFiles(); File *file; AUTO_VAR(itEnd, subFolders->end()); for (AUTO_VAR(it, subFolders->begin()); it != itEnd; it++) { file = *it; //subFolders->at(i); if (file->isDirectory()) { continue; } wstring levelId = file->getName(); LevelData *levelData = getDataTagFor(levelId); if (levelData != NULL) { bool requiresConversion = levelData->getVersion() != McRegionLevelStorage::MCREGION_VERSION_ID; wstring levelName = levelData->getLevelName(); if (levelName.empty()) // 4J Jev TODO: levelName can't be NULL? if (levelName == NULL || isEmpty(levelName)) { levelName = levelId; } // long size = getLevelSize(folder); long size = 0; levels->push_back(new LevelSummary(levelId, levelName, levelData->getLastPlayed(), size, requiresConversion, levelData->isHardcore())); } } #endif return levels; } void McRegionLevelStorageSource::clearAll() { } shared_ptr McRegionLevelStorageSource::selectLevel(ConsoleSaveFile *saveFile, const wstring& levelId, bool createPlayerDir) { // return new LevelStorageProfilerDecorator(new McRegionLevelStorage(baseDir, levelId, createPlayerDir)); return shared_ptr(new McRegionLevelStorage(saveFile, baseDir, levelId, createPlayerDir)); } bool McRegionLevelStorageSource::isConvertible(ConsoleSaveFile *saveFile, const wstring& levelId) { // check if there is old file format level data LevelData *levelData = getDataTagFor(saveFile, levelId); if (levelData == NULL || levelData->getVersion() != 0) { delete levelData; return false; } delete levelData; return true; } bool McRegionLevelStorageSource::requiresConversion(ConsoleSaveFile *saveFile, const wstring& levelId) { LevelData *levelData = getDataTagFor(saveFile, levelId); if (levelData == NULL || levelData->getVersion() != 0) { delete levelData; return false; } delete levelData; return true; } bool McRegionLevelStorageSource::convertLevel(ConsoleSaveFile *saveFile, const wstring& levelId, ProgressListener *progress) { assert(false); // I removed this while updating the saves to use the single save file // Will we ever use this convertLevel function anyway? The main issue is the check // for the hellFolder.exists() which would require a slight change to the way our // save files are structured #if 0 progress->progressStagePercentage(0); vector *normalRegions = new vector; vector *normalBaseFolders = new vector; vector *netherRegions = new vector; vector *netherBaseFolders = new vector; ArrayList enderRegions = new ArrayList(); ArrayList enderBaseFolders = new ArrayList(); //File baseFolder = File(baseDir, levelId); //File netherFolder = File(baseFolder, LevelStorage::HELL_FOLDER); //File enderFolder = new File(baseFolder, LevelStorage.ENDER_FOLDER); ConsoleSaveFile saveFile = ConsoleSaveFile( levelId ); // System.out.println("Scanning folders..."); 4J Jev, TODO how do we println ? // find normal world addRegions(baseFolder, normalRegions, normalBaseFolders); // find hell world if (netherFolder.exists()) { addRegions(netherFolder, netherRegions, netherBaseFolders); } if (enderFolder.exists()) { addRegions(enderFolder, enderRegions, enderBaseFolders); } int totalCount = normalRegions->size() + netherRegions->size() + enderRegions.size() + normalBaseFolders->size() + netherBaseFolders->size() + enderBaseFolders.size(); // System.out.println("Total conversion count is " + totalCount); 4J Jev, TODO // convert normal world convertRegions(baseFolder, normalRegions, 0, totalCount, progress); // convert hell world convertRegions(netherFolder, netherRegions, normalRegions->size(), totalCount, progress); // convert hell world convertRegions(enderFolder, enderRegions, normalRegions.size() + netherRegions.size(), totalCount, progress); LevelData *levelData = getDataTagFor(levelId); levelData->setVersion(McRegionLevelStorage::MCREGION_VERSION_ID); LevelStorage *levelStorage = selectLevel(levelId, false); levelStorage->saveLevelData(levelData); // erase old files eraseFolders(normalBaseFolders, normalRegions->size() + netherRegions->size(), totalCount, progress); if (netherFolder.exists()) { eraseFolders(netherBaseFolders, normalRegions->size() + netherRegions->size() + normalBaseFolders->size(), totalCount, progress); } #endif return true; } #if 0 // 4J - not required anymore void McRegionLevelStorageSource::addRegions(File &baseFolder, vector *dest, vector *firstLevelFolders) { FolderFilter folderFilter; ChunkFilter chunkFilter; File *folder1; vector *folderLevel1 = baseFolder.listFiles((FileFilter *) &folderFilter); AUTO_VAR(itEnd, folderLevel1->end()); for (AUTO_VAR(it, folderLevel1->begin()); it != itEnd; it++) { folder1 = *it; //folderLevel1->at(i1); // keep this for the clean-up process later on firstLevelFolders->push_back(folder1); File *folder2; vector *folderLevel2 = folder1->listFiles(&folderFilter); AUTO_VAR(itEnd2, folderLevel2->end()); for (AUTO_VAR(it2, folderLevel2->begin()); it2 != itEnd; it2++) { folder2 = *it2; //folderLevel2->at(i2); vector *chunkFiles = folder2->listFiles((FileFilter *) &chunkFilter); File *chunk; AUTO_VAR(itEndFile, chunkFiles->end()); for (AUTO_VAR(itFile, chunkFiles->begin()); itFile != itEndFile; itFile++) { chunk = *itFile; //chunkFiles->at(i3); dest->push_back(new ChunkFile(chunk)); } } } } #endif void McRegionLevelStorageSource::convertRegions(File &baseFolder, vector *chunkFiles, int currentCount, int totalCount, ProgressListener *progress) { assert( false ); // 4J Stu - Removed, see comment in convertLevel above #if 0 //Collections::sort(chunkFiles); std::sort( chunkFiles->begin(), chunkFiles->end() ); byteArray buffer = byteArray(4096); ChunkFile *chunkFile; AUTO_VAR(itEnd, chunkFiles->end()); for (AUTO_VAR(it, chunkFiles->begin()); it != itEnd; it++) { chunkFile = *it; //chunkFiles->at(i1); // Matcher matcher = ChunkFilter.chunkFilePattern.matcher(chunkFile.getName()); // if (!matcher.matches()) { // continue; // } // int x = Integer.parseInt(matcher.group(1), 36); // int z = Integer.parseInt(matcher.group(2), 36); int x = chunkFile->getX(); int z = chunkFile->getZ(); RegionFile *region = RegionFileCache::getRegionFile(baseFolder, x, z); if (!region->hasChunk(x & 31, z & 31)) { FileInputStream fis = new BufferedInputStream(FileInputStream(*chunkFile->getFile())); DataInputStream istream = DataInputStream(&fis); // 4J - was new GZIPInputStream as well DataOutputStream *out = region->getChunkDataOutputStream(x & 31, z & 31); int length = 0; while ( (length = istream.read(buffer)) != -1 ) { out->write(buffer, 0, length); } out->close(); istream.close(); // 4J Stu - getChunkDataOutputStream makes a new DataOutputStream that points to a new ChunkBuffer( ByteArrayOutputStream ) // We should clean these up when we are done out->deleteChildStream(); delete out; } currentCount++; int percent = (int) Math::round(100.0 * (double) currentCount / (double) totalCount); progress->progressStagePercentage(percent); } RegionFileCache::clear(); #endif } void McRegionLevelStorageSource::eraseFolders(vector *folders, int currentCount, int totalCount, ProgressListener *progress) { File *folder; AUTO_VAR(itEnd, folders->end()); for (AUTO_VAR(it, folders->begin()); it != itEnd; it++) { folder = *it; //folders->at(i); vector *files = folder->listFiles(); deleteRecursive(files); folder->_delete(); currentCount++; int percent = (int) Math::round(100.0 * (double) currentCount / (double) totalCount); progress->progressStagePercentage(percent); } } #if 0 // 4J - not required anymore bool McRegionLevelStorageSource::FolderFilter::accept(File *file) { if (file->isDirectory()) { Matcher matcher( chunkFolderPattern, file->getName() ); return matcher.matches(); } return false; } bool McRegionLevelStorageSource::ChunkFilter::accept(File *dir, const wstring& name) { Matcher matcher( chunkFilePattern, name ); return matcher.matches(); } McRegionLevelStorageSource::ChunkFile::ChunkFile(File *file) { this->file = file; Matcher matcher( ChunkFilter::chunkFilePattern, file->getName() ); if (matcher.matches()) { x = Integer::parseInt(matcher.group(1), 36); z = Integer::parseInt(matcher.group(2), 36); } else { x = 0; z = 0; } } //Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. int McRegionLevelStorageSource::ChunkFile::compareTo(ChunkFile *rhs) { // sort chunk files so that they are placed according to their // region position int rx = x >> 5; int rhsrx = rhs->x >> 5; if (rx == rhsrx) { int rz = z >> 5; int rhsrz = rhs->z >> 5; return rz - rhsrz; } return rx - rhsrx; } // 4J Stu Added so we can use std::sort instead of the java Collections::sort // a < b bool McRegionLevelStorageSource::ChunkFile::operator<( ChunkFile *b ) { return compareTo( b ) < 0; } File *McRegionLevelStorageSource::ChunkFile::getFile() { return (File *) file; } int McRegionLevelStorageSource::ChunkFile::getX() { return x; } int McRegionLevelStorageSource::ChunkFile::getZ() { return z; } #endif