the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "net.minecraft.world.level.chunk.storage.h"
3#include "net.minecraft.world.level.storage.h"
4#include "ConsoleSaveFileIO.h"
5#include "ConsoleSaveFileConverter.h"
6#include "ProgressListener.h"
7
8void ConsoleSaveFileConverter::ProcessSimpleFile(ConsoleSaveFile *sourceSave, FileEntry *sourceFileEntry, ConsoleSaveFile *targetSave, FileEntry *targetFileEntry)
9{
10 DWORD numberOfBytesRead = 0;
11 DWORD numberOfBytesWritten = 0;
12
13 byte *data = new byte[sourceFileEntry->getFileSize()];
14
15 // Read from source
16 sourceSave->readFile(sourceFileEntry, data, sourceFileEntry->getFileSize(), &numberOfBytesRead);
17
18 // Write back to target
19 targetSave->writeFile(targetFileEntry, data, numberOfBytesRead, &numberOfBytesWritten);
20
21 delete [] data;
22}
23
24void ConsoleSaveFileConverter::ProcessStandardRegionFile(ConsoleSaveFile *sourceSave, File sourceFile, ConsoleSaveFile *targetSave, File targetFile)
25{
26 DWORD numberOfBytesWritten = 0;
27 DWORD numberOfBytesRead = 0;
28
29 RegionFile sourceRegionFile(sourceSave, &sourceFile);
30 RegionFile targetRegionFile(targetSave, &targetFile);
31
32 for(unsigned int x = 0; x < 32; ++x)
33 {
34 for(unsigned int z = 0; z < 32; ++z)
35 {
36 DataInputStream *dis = sourceRegionFile.getChunkDataInputStream(x,z);
37
38 if(dis)
39 {
40 int read = dis->read();
41 DataOutputStream *dos = targetRegionFile.getChunkDataOutputStream(x,z);
42 while(read != -1)
43 {
44
45 dos->write( read & 0xff );
46
47 read = dis->read();
48 }
49 dos->close();
50 dos->deleteChildStream();
51 delete dos;
52 }
53
54 delete dis;
55 }
56 }
57}
58
59void ConsoleSaveFileConverter::ConvertSave(ConsoleSaveFile *sourceSave, ConsoleSaveFile *targetSave, ProgressListener *progress)
60{
61 // Process level.dat
62 ConsoleSavePath ldatPath( wstring(L"level.dat") );
63 FileEntry *sourceLdatFe = sourceSave->createFile( ldatPath );
64 FileEntry *targetLdatFe = targetSave->createFile( ldatPath );
65 app.DebugPrintf("Processing level.dat\n");
66 ProcessSimpleFile(sourceSave, sourceLdatFe, targetSave, targetLdatFe);
67
68 // Process game rules
69 {
70 ConsoleSavePath gameRulesPath( GAME_RULE_SAVENAME );
71 if(sourceSave->doesFileExist(gameRulesPath) )
72 {
73 FileEntry *sourceFe = sourceSave->createFile( gameRulesPath );
74 FileEntry *targetFe = targetSave->createFile( gameRulesPath );
75 app.DebugPrintf("Processing game rules\n");
76 ProcessSimpleFile(sourceSave, sourceFe, targetSave, targetFe);
77 }
78 }
79
80 // MGH added - find any player data files and copy them across
81#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__)
82 vector<FileEntry *>* playerFiles = sourceSave->getValidPlayerDatFiles();
83#else
84 vector<FileEntry *> *playerFiles = sourceSave->getFilesWithPrefix( DirectoryLevelStorage::getPlayerDir() );
85#endif
86
87 if(playerFiles != NULL)
88 {
89 for(int fileIdx = 0; fileIdx < playerFiles->size();fileIdx++)
90 {
91 ConsoleSavePath sourcePlayerDatPath( playerFiles->at(fileIdx)->data.filename );
92#ifdef _XBOX_ONE
93 // 4J Stu - As the XUIDs on X360 and X1 are different, we don't want to transfer these over. However as the first player
94 // file should be the owner of the save, we can move their data over to the current players XUID
95 if(fileIdx > 0) break;
96 PlayerUID xuid;
97 ProfileManager.GetXUID(ProfileManager.GetPrimaryPad(), &xuid, false);
98 ConsoleSavePath targetPlayerDatPath( L"players/" + xuid.toString() + L".dat" );
99#else
100 ConsoleSavePath targetPlayerDatPath( playerFiles->at(fileIdx)->data.filename );
101#endif
102 {
103 FileEntry *sourceFe = sourceSave->createFile( sourcePlayerDatPath );
104 FileEntry *targetFe = targetSave->createFile( targetPlayerDatPath );
105 app.DebugPrintf("Processing player dat file %s\n", playerFiles->at(fileIdx)->data.filename);
106 ProcessSimpleFile(sourceSave, sourceFe, targetSave, targetFe);
107
108 targetFe->data.lastModifiedTime = sourceFe->data.lastModifiedTime;
109 }
110 }
111 delete playerFiles;
112 }
113
114
115#ifdef SPLIT_SAVES
116 int xzSize = LEVEL_LEGACY_WIDTH;
117 int hellScale = HELL_LEVEL_LEGACY_SCALE;
118 if ( sourceSave->doesFileExist( ldatPath ) )
119 {
120 ConsoleSaveFileInputStream fis = ConsoleSaveFileInputStream(sourceSave, ldatPath);
121 CompoundTag *root = NbtIo::readCompressed(&fis);
122 CompoundTag *tag = root->getCompound(L"Data");
123 LevelData ret(tag);
124
125 xzSize = ret.getXZSize();
126 hellScale = ret.getHellScale();
127
128 delete root;
129 }
130
131 RegionFileCache sourceCache;
132 RegionFileCache targetCache;
133
134 if(progress)
135 {
136#ifndef _WINDOWS64
137 progress->progressStage(IDS_SAVETRANSFER_STAGE_CONVERTING);
138#endif
139 }
140
141 // Overworld
142 {
143 app.DebugPrintf("Processing the overworld\n");
144 int halfXZSize = xzSize / 2;
145
146 int progressTarget = (xzSize) * (xzSize);
147 int currentProgress = 0;
148 if(progress) progress->progressStagePercentage((currentProgress*100)/progressTarget);
149
150 for(int x = -halfXZSize; x < halfXZSize; ++x)
151 {
152 for(int z = -halfXZSize; z < halfXZSize; ++z)
153 {
154 //app.DebugPrintf("Processing overworld chunk %d,%d\n",x,z);
155 DataInputStream *dis = sourceCache._getChunkDataInputStream(sourceSave,L"",x,z);
156
157 if(dis)
158 {
159 int read = dis->read();
160 DataOutputStream *dos = targetCache._getChunkDataOutputStream(targetSave,L"",x,z);
161 BufferedOutputStream bos(dos, 1024 * 1024);
162 while(read != -1)
163 {
164
165 bos.write( read & 0xff );
166
167 read = dis->read();
168 }
169 bos.flush();
170 dos->close();
171 dos->deleteChildStream();
172 delete dos;
173 dis->deleteChildStream();
174 delete dis;
175 }
176
177
178 ++currentProgress;
179 if(progress) progress->progressStagePercentage( (currentProgress*100)/progressTarget);
180
181 }
182 }
183 }
184
185 // Nether
186 {
187 app.DebugPrintf("Processing the nether\n");
188 int hellSize = xzSize / hellScale;
189 int halfXZSize = hellSize / 2;
190
191 int progressTarget = (hellSize) * (hellSize);
192 int currentProgress = 0;
193 if(progress) progress->progressStagePercentage((currentProgress*100)/progressTarget);
194
195 for(int x = -halfXZSize; x < halfXZSize; ++x)
196 {
197 for(int z = -halfXZSize; z < halfXZSize; ++z)
198 {
199 //app.DebugPrintf("Processing nether chunk %d,%d\n",x,z);
200 DataInputStream *dis = sourceCache._getChunkDataInputStream(sourceSave,L"DIM-1",x,z);
201
202 if(dis)
203 {
204 int read = dis->read();
205 DataOutputStream *dos = targetCache._getChunkDataOutputStream(targetSave,L"DIM-1",x,z);
206 BufferedOutputStream bos(dos, 1024 * 1024);
207 while(read != -1)
208 {
209
210 bos.write( read & 0xff );
211
212 read = dis->read();
213 }
214 bos.flush();
215 dos->close();
216 dos->deleteChildStream();
217 delete dos;
218 dis->deleteChildStream();
219 delete dis;
220 }
221
222
223 ++currentProgress;
224 if(progress) progress->progressStagePercentage((currentProgress*100)/progressTarget);
225 }
226 }
227 }
228
229 // End
230 {
231 app.DebugPrintf("Processing the end\n");
232 int halfXZSize = END_LEVEL_MAX_WIDTH / 2;
233
234 int progressTarget = (END_LEVEL_MAX_WIDTH) * (END_LEVEL_MAX_WIDTH);
235 int currentProgress = 0;
236 if(progress) progress->progressStagePercentage((currentProgress*100)/progressTarget);
237
238 for(int x = -halfXZSize; x < halfXZSize; ++x)
239 {
240 for(int z = -halfXZSize; z < halfXZSize; ++z)
241 {
242 //app.DebugPrintf("Processing end chunk %d,%d\n",x,z);
243 DataInputStream *dis = sourceCache._getChunkDataInputStream(sourceSave,L"DIM1/",x,z);
244
245 if(dis)
246 {
247 int read = dis->read();
248 DataOutputStream *dos = targetCache._getChunkDataOutputStream(targetSave,L"DIM1/",x,z);
249 BufferedOutputStream bos(dos, 1024 * 1024);
250 while(read != -1)
251 {
252
253 bos.write( read & 0xff );
254
255 read = dis->read();
256 }
257 bos.flush();
258 dos->close();
259 dos->deleteChildStream();
260 delete dos;
261 dis->deleteChildStream();
262 delete dis;
263 }
264
265
266 ++currentProgress;
267 if(progress) progress->progressStagePercentage((currentProgress*100)/progressTarget);
268 }
269 }
270 }
271
272#else
273 // 4J Stu - Old version that just changes the compression of chunks, not usable for XboxOne style split saves or compressed tile formats
274 // Process region files
275 vector<FileEntry *> *allFilesInSave = sourceSave->getFilesWithPrefix(wstring(L""));
276 for(AUTO_VAR(it, allFilesInSave->begin()); it < allFilesInSave->end(); ++it)
277 {
278 FileEntry *fe = *it;
279 if( fe != sourceLdatFe )
280 {
281 wstring fName( fe->data.filename );
282 wstring suffix(L".mcr");
283 if( fName.compare(fName.length() - suffix.length(), suffix.length(), suffix) == 0 )
284 {
285#ifndef _CONTENT_PACKAGE
286 wprintf(L"Processing a region file: %s\n", fe->data.filename);
287#endif
288 ProcessStandardRegionFile(sourceSave, File(fe->data.filename), targetSave, File(fe->data.filename) );
289 }
290 else
291 {
292#ifndef _CONTENT_PACKAGE
293 wprintf(L"%s is not a region file, ignoring\n", fe->data.filename);
294#endif
295 }
296 }
297 }
298#endif
299}