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 "StringHelpers.h"
3#include "ConsoleSaveFileOriginal.h"
4#include "File.h"
5#include <xuiapp.h>
6#include "compression.h"
7#include "..\Minecraft.Client\Minecraft.h"
8#include "..\Minecraft.Client\MinecraftServer.h"
9#include "..\Minecraft.Client\ServerLevel.h"
10#include "..\Minecraft.World\net.minecraft.world.level.h"
11#include "..\Minecraft.World\LevelData.h"
12#include "..\Minecraft.Client\Common\GameRules\LevelGenerationOptions.h"
13#include "..\Minecraft.World\net.minecraft.world.level.chunk.storage.h"
14
15
16#ifdef _XBOX
17#define RESERVE_ALLOCATION MEM_RESERVE | MEM_LARGE_PAGES
18#define COMMIT_ALLOCATION MEM_COMMIT | MEM_LARGE_PAGES
19#else
20#define RESERVE_ALLOCATION MEM_RESERVE
21#define COMMIT_ALLOCATION MEM_COMMIT
22#endif
23
24unsigned int ConsoleSaveFileOriginal::pagesCommitted = 0;
25void *ConsoleSaveFileOriginal::pvHeap = NULL;
26
27ConsoleSaveFileOriginal::ConsoleSaveFileOriginal(const wstring &fileName, LPVOID pvSaveData /*= NULL*/, DWORD dFileSize /*= 0*/, bool forceCleanSave /*= false*/, ESavePlatform plat /*= SAVE_FILE_PLATFORM_LOCAL*/)
28{
29 InitializeCriticalSectionAndSpinCount(&m_lock,5120);
30
31 // One time initialise of static stuff required for our storage
32 if( pvHeap == NULL )
33 {
34 // Reserve a chunk of 64MB of virtual address space for our saves, using 64KB pages.
35 // We'll only be committing these as required to grow the storage we need, which will
36 // the storage to grow without having to use realloc.
37
38 // AP - The Vita doesn't have virtual memory so a pretend system has been implemented in PSVitaStubs.cpp.
39 // All access to the memory must be done via the access function as the pointer returned from VirtualAlloc
40 // can't be used directly.
41 pvHeap = VirtualAlloc(NULL, MAX_PAGE_COUNT * CSF_PAGE_SIZE, RESERVE_ALLOCATION, PAGE_READWRITE );
42 }
43
44 pvSaveMem = pvHeap;
45 m_fileName = fileName;
46
47 DWORD fileSize = dFileSize;
48
49 // Load a save from the game rules
50 bool bLevelGenBaseSave = false;
51 LevelGenerationOptions *levelGen = app.getLevelGenerationOptions();
52 if( pvSaveData == NULL && levelGen != NULL && levelGen->requiresBaseSave())
53 {
54 pvSaveData = levelGen->getBaseSaveData(fileSize);
55 if(pvSaveData && fileSize != 0) bLevelGenBaseSave = true;
56 }
57
58 if( pvSaveData == NULL || fileSize == 0)
59 fileSize = StorageManager.GetSaveSize();
60
61 if( forceCleanSave )
62 fileSize = 0;
63
64 DWORD heapSize = max( fileSize, (DWORD)(1024 * 1024 * 2)); // 4J Stu - Our files are going to be bigger than 2MB so allocate high to start with
65
66 // 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,
67 // and the pages should be decommitted in the dtor, so pages committed should always be zero at this point.
68 if( pagesCommitted != 0 )
69 {
70#ifndef _CONTENT_PACKAGE
71 __debugbreak();
72#endif
73 }
74
75 unsigned int pagesRequired = ( heapSize + (CSF_PAGE_SIZE - 1 ) ) / CSF_PAGE_SIZE;
76
77 void *pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE, COMMIT_ALLOCATION, PAGE_READWRITE);
78 if( pvRet == NULL )
79 {
80#ifndef _CONTENT_PACKAGE
81 // Out of physical memory
82 __debugbreak();
83#endif
84 }
85 pagesCommitted = pagesRequired;
86
87 if( fileSize > 0)
88 {
89 bool AllocData = false;
90 if(pvSaveData != NULL)
91 {
92#ifdef __PSVITA__
93 // AP - use this to access the virtual memory
94 VirtualCopyTo(pvSaveMem, pvSaveData, fileSize);
95#else
96 memcpy(pvSaveMem, pvSaveData, fileSize);
97 if(bLevelGenBaseSave)
98 {
99 levelGen->deleteBaseSaveData();
100 }
101#endif
102 }
103 else
104 {
105 unsigned int storageLength;
106#ifdef __PSVITA__
107 // create a buffer to hold the compressed data
108 pvSaveData = malloc(fileSize);
109 AllocData = true;
110 StorageManager.GetSaveData( pvSaveData, &storageLength );
111#else
112 StorageManager.GetSaveData( pvSaveMem, &storageLength );
113#endif
114#ifdef __PS3__
115 StorageManager.FreeSaveData();
116#endif
117 app.DebugPrintf("Filesize - %d, Adjusted size - %d\n",fileSize,storageLength);
118 fileSize = storageLength;
119 }
120
121#ifdef __PSVITA__
122 if(plat == SAVE_FILE_PLATFORM_PSVITA)
123 {
124 // AP - decompress via the access function. This uses a special RLE format
125 VirtualDecompress((unsigned char *)pvSaveData+8, fileSize-8 );
126 if( AllocData )
127 {
128 // free the compressed data buffer if required
129 free( pvSaveData );
130 }
131 else if(bLevelGenBaseSave)
132 {
133 levelGen->deleteBaseSaveData();
134 }
135 }
136 else
137#endif
138 {
139#ifdef __PSVITA__
140 void* pvSourceData = pvSaveData;
141#else
142 void* pvSourceData = pvSaveMem;
143#endif
144 int compressed = *(int*)pvSourceData;
145 if( compressed == 0 )
146 {
147 unsigned int decompSize = *( (int*)pvSourceData+1 );
148 if(isLocalEndianDifferent(plat)) System::ReverseULONG(&decompSize);
149
150 // An invalid save, so clear the memory and start from scratch
151 if(decompSize == 0)
152 {
153 // 4J Stu - Saves created between 2/12/2011 and 7/12/2011 will have this problem
154 app.DebugPrintf("Invalid save data format\n");
155 ZeroMemory( pvSourceData, fileSize );
156 // Clear the first 8 bytes that reference the header
157 header.WriteHeader( pvSourceData );
158 }
159 else
160 {
161 unsigned char *buf = new unsigned char[decompSize];
162#ifndef _XBOX
163 if(plat == SAVE_FILE_PLATFORM_PSVITA)
164 {
165 Compression::VitaVirtualDecompress(buf, &decompSize, (unsigned char *)pvSourceData+8, fileSize-8 );
166 }
167 else
168#endif
169 {
170 Compression::getCompression()->SetDecompressionType(plat); // if this save is from another platform, set the correct decompression type
171 Compression::getCompression()->Decompress(buf, &decompSize, (unsigned char *)pvSourceData+8, fileSize-8 );
172 Compression::getCompression()->SetDecompressionType(SAVE_FILE_PLATFORM_LOCAL); // and then set the decompression back to the local machine's standard type
173 }
174
175 // Only ReAlloc if we need to (we might already have enough) and align to 512 byte boundaries
176 DWORD currentHeapSize = pagesCommitted * CSF_PAGE_SIZE;
177
178 DWORD desiredSize = decompSize;
179
180 if( desiredSize > currentHeapSize )
181 {
182 unsigned int pagesRequired = ( desiredSize + (CSF_PAGE_SIZE - 1 ) ) / CSF_PAGE_SIZE;
183 void *pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE, COMMIT_ALLOCATION, PAGE_READWRITE);
184 if( pvRet == NULL )
185 {
186 // Out of physical memory
187 __debugbreak();
188 }
189 pagesCommitted = pagesRequired;
190 }
191#ifdef __PSVITA__
192 VirtualCopyTo(pvSaveMem, buf, decompSize);
193#else
194 memcpy(pvSaveMem, buf, decompSize);
195#endif
196 delete[] buf;
197 }
198 }
199 }
200
201 header.ReadHeader( pvSaveMem, plat );
202
203 }
204 else
205 {
206 // Clear the first 8 bytes that reference the header
207 header.WriteHeader( pvSaveMem );
208 }
209}
210
211ConsoleSaveFileOriginal::~ConsoleSaveFileOriginal()
212{
213 VirtualFree( pvHeap, MAX_PAGE_COUNT * CSF_PAGE_SIZE, MEM_DECOMMIT );
214 pagesCommitted = 0;
215 // Make sure we don't have any thumbnail data still waiting round - we can't need it now we've destroyed the save file anyway
216#if defined _XBOX
217 app.GetSaveThumbnail(NULL,NULL);
218#elif defined __PS3__
219 app.GetSaveThumbnail(NULL,NULL, NULL,NULL);
220#endif
221
222 DeleteCriticalSection(&m_lock);
223}
224
225// Add the file to our table of internal files if not already there
226// Open our actual save file ready for reading/writing, and the set the file pointer to the start of this file
227FileEntry *ConsoleSaveFileOriginal::createFile( const ConsoleSavePath &fileName )
228{
229 LockSaveAccess();
230 FileEntry *file = header.AddFile( fileName.getName() );
231 ReleaseSaveAccess();
232
233 return file;
234}
235
236void ConsoleSaveFileOriginal::deleteFile( FileEntry *file )
237{
238 if( file == NULL ) return;
239
240 LockSaveAccess();
241
242 DWORD numberOfBytesRead = 0;
243 DWORD numberOfBytesWritten = 0;
244
245 const int bufferSize = 4096;
246 int amountToRead = bufferSize;
247 byte buffer[bufferSize];
248 DWORD bufferDataSize = 0;
249
250
251 char *readStartOffset = (char *)pvSaveMem + file->data.startOffset + file->getFileSize();
252
253 char *writeStartOffset = (char *)pvSaveMem + file->data.startOffset;
254
255 char *endOfDataOffset = (char *)pvSaveMem + header.GetStartOfNextData();
256
257 while(true)
258 {
259 // Fill buffer from file
260 if( readStartOffset + bufferSize > endOfDataOffset )
261 {
262 amountToRead = (int)(endOfDataOffset - readStartOffset);
263 }
264 else
265 {
266 amountToRead = bufferSize;
267 }
268
269 if( amountToRead == 0 )
270 break;
271
272#ifdef __PSVITA__
273 // AP - use this to access the virtual memory
274 VirtualCopyFrom( buffer, readStartOffset, amountToRead );
275#else
276 memcpy( buffer, readStartOffset, amountToRead );
277#endif
278 numberOfBytesRead = amountToRead;
279
280 bufferDataSize = amountToRead;
281 readStartOffset += numberOfBytesRead;
282
283 // Write buffer to file
284#ifdef __PSVITA__
285 // AP - use this to access the virtual memory
286 VirtualCopyTo( (void *)writeStartOffset, buffer, bufferDataSize );
287#else
288 memcpy( (void *)writeStartOffset, buffer, bufferDataSize );
289#endif
290 numberOfBytesWritten = bufferDataSize;
291
292 writeStartOffset += numberOfBytesWritten;
293 }
294
295 header.RemoveFile( file );
296
297 finalizeWrite();
298
299 ReleaseSaveAccess();
300}
301
302void ConsoleSaveFileOriginal::setFilePointer(FileEntry *file,LONG lDistanceToMove,PLONG lpDistanceToMoveHigh,DWORD dwMoveMethod)
303{
304 LockSaveAccess();
305
306 file->currentFilePointer = file->data.startOffset + lDistanceToMove;
307
308 if( dwMoveMethod == FILE_END)
309 {
310 file->currentFilePointer += file->getFileSize();
311 }
312
313 ReleaseSaveAccess();
314}
315
316// If this file needs to grow, move the data after along
317void ConsoleSaveFileOriginal::PrepareForWrite( FileEntry *file, DWORD nNumberOfBytesToWrite )
318{
319 int bytesToGrowBy = ( (file->currentFilePointer - file->data.startOffset) + nNumberOfBytesToWrite) - file->getFileSize();
320 if( bytesToGrowBy <= 0 )
321 return;
322
323 // 4J Stu - Not forcing a minimum size, it is up to the caller to write data in sensible amounts
324 // This lets us keep some of the smaller files small
325 //if( bytesToGrowBy < 1024 )
326 // bytesToGrowBy = 1024;
327
328 // Move all the data beyond us
329 MoveDataBeyond(file, bytesToGrowBy);
330
331 // Update our length
332 if( file->data.length < 0 )
333 file->data.length = 0;
334 file->data.length += bytesToGrowBy;
335
336 // Write the header with the updated data
337 finalizeWrite();
338}
339
340BOOL ConsoleSaveFileOriginal::writeFile(FileEntry *file,LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten)
341{
342 assert( pvSaveMem != NULL );
343 if( pvSaveMem == NULL )
344 {
345 return 0;
346 }
347
348 LockSaveAccess();
349
350 PrepareForWrite( file, nNumberOfBytesToWrite );
351
352 char *writeStartOffset = (char *)pvSaveMem + file->currentFilePointer;
353 //printf("Write: pvSaveMem = %0xd, currentFilePointer = %d, writeStartOffset = %0xd\n", pvSaveMem, file->currentFilePointer, writeStartOffset);
354
355#ifdef __PSVITA__
356 // AP - use this to access the virtual memory
357 VirtualCopyTo((void *)writeStartOffset, (void*)lpBuffer, nNumberOfBytesToWrite);
358#else
359 memcpy( (void *)writeStartOffset, lpBuffer, nNumberOfBytesToWrite );
360#endif
361 *lpNumberOfBytesWritten = nNumberOfBytesToWrite;
362
363 if(file->data.length < 0)
364 file->data.length = 0;
365
366 file->currentFilePointer += *lpNumberOfBytesWritten;
367
368 //wprintf(L"Wrote %d bytes to %s, new file pointer is %I64d\n", *lpNumberOfBytesWritten, file->data.filename, file->currentFilePointer);
369
370 file->updateLastModifiedTime();
371
372 ReleaseSaveAccess();
373
374 return 1;
375}
376
377BOOL ConsoleSaveFileOriginal::zeroFile(FileEntry *file, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten)
378{
379 assert( pvSaveMem != NULL );
380 if( pvSaveMem == NULL )
381 {
382 return 0;
383 }
384
385 LockSaveAccess();
386
387 PrepareForWrite( file, nNumberOfBytesToWrite );
388
389 char *writeStartOffset = (char *)pvSaveMem + file->currentFilePointer;
390 //printf("Write: pvSaveMem = %0xd, currentFilePointer = %d, writeStartOffset = %0xd\n", pvSaveMem, file->currentFilePointer, writeStartOffset);
391
392#ifdef __PSVITA__
393 // AP - use this to access the virtual memory
394 VirtualMemset( (void *)writeStartOffset, 0, nNumberOfBytesToWrite );
395#else
396 memset( (void *)writeStartOffset, 0, nNumberOfBytesToWrite );
397#endif
398 *lpNumberOfBytesWritten = nNumberOfBytesToWrite;
399
400 if(file->data.length < 0)
401 file->data.length = 0;
402
403 file->currentFilePointer += *lpNumberOfBytesWritten;
404
405 //wprintf(L"Wrote %d bytes to %s, new file pointer is %I64d\n", *lpNumberOfBytesWritten, file->data.filename, file->currentFilePointer);
406
407 file->updateLastModifiedTime();
408
409 ReleaseSaveAccess();
410
411 return 1;
412}
413
414BOOL ConsoleSaveFileOriginal::readFile( FileEntry *file, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead)
415{
416 DWORD actualBytesToRead;
417 assert( pvSaveMem != NULL );
418 if( pvSaveMem == NULL )
419 {
420 return 0;
421 }
422
423 LockSaveAccess();
424
425 char *readStartOffset = (char *)pvSaveMem + file->currentFilePointer;
426 //printf("Read: pvSaveMem = %0xd, currentFilePointer = %d, readStartOffset = %0xd\n", pvSaveMem, file->currentFilePointer, readStartOffset);
427
428 assert( nNumberOfBytesToRead <= file->getFileSize() );
429
430 actualBytesToRead = nNumberOfBytesToRead;
431 if( file->currentFilePointer + nNumberOfBytesToRead > file->data.startOffset + file->data.length )
432 {
433 actualBytesToRead = (file->data.startOffset + file->data.length) - file->currentFilePointer;
434 }
435
436#ifdef __PSVITA__
437 // AP - use this to access the virtual memory
438 VirtualCopyFrom( lpBuffer, readStartOffset, actualBytesToRead );
439#else
440 memcpy( lpBuffer, readStartOffset, actualBytesToRead );
441#endif
442
443 *lpNumberOfBytesRead = actualBytesToRead;
444
445 file->currentFilePointer += *lpNumberOfBytesRead;
446
447 //wprintf(L"Read %d bytes from %s, new file pointer is %I64d\n", *lpNumberOfBytesRead, file->data.filename, file->currentFilePointer);
448
449 ReleaseSaveAccess();
450
451 return 1;
452}
453
454BOOL ConsoleSaveFileOriginal::closeHandle( FileEntry *file )
455{
456 LockSaveAccess();
457 finalizeWrite();
458 ReleaseSaveAccess();
459
460 return TRUE;
461}
462
463void ConsoleSaveFileOriginal::finalizeWrite()
464{
465 LockSaveAccess();
466 header.WriteHeader( pvSaveMem );
467 ReleaseSaveAccess();
468}
469
470void ConsoleSaveFileOriginal::MoveDataBeyond(FileEntry *file, DWORD nNumberOfBytesToWrite)
471{
472 DWORD numberOfBytesRead = 0;
473 DWORD numberOfBytesWritten = 0;
474
475 const DWORD bufferSize = 4096;
476 DWORD amountToRead = bufferSize;
477 //assert( nNumberOfBytesToWrite <= bufferSize );
478 static byte buffer1[bufferSize];
479 static byte buffer2[bufferSize];
480 DWORD buffer1Size = 0;
481 DWORD buffer2Size = 0;
482
483 // Only ReAlloc if we need to (we might already have enough) and align to 512 byte boundaries
484 DWORD currentHeapSize = pagesCommitted * CSF_PAGE_SIZE;
485
486 DWORD desiredSize = header.GetFileSize() + nNumberOfBytesToWrite;
487
488 if( desiredSize > currentHeapSize )
489 {
490 unsigned int pagesRequired = ( desiredSize + (CSF_PAGE_SIZE - 1 ) ) / CSF_PAGE_SIZE;
491 void *pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE, COMMIT_ALLOCATION, PAGE_READWRITE);
492 if( pvRet == NULL )
493 {
494 // Out of physical memory
495 __debugbreak();
496 }
497 pagesCommitted = pagesRequired;
498 }
499
500 // This is the start of where we want the space to be, and the start of the data that we need to move
501 char *spaceStartOffset = (char *)pvSaveMem + file->data.startOffset + file->getFileSize();
502
503 // This is the end of where we want the space to be
504 char *spaceEndOffset = spaceStartOffset + nNumberOfBytesToWrite;
505
506 // This is the current end of the data that we want to move
507 char *beginEndOfDataOffset = (char *)pvSaveMem + header.GetStartOfNextData();
508
509 // This is where the end of the data is going to be
510 char *finishEndOfDataOffset = beginEndOfDataOffset + nNumberOfBytesToWrite;
511
512 // This is where we are going to read from (with the amount we want to read subtracted before we read)
513 char *readStartOffset = beginEndOfDataOffset;
514
515 // This is where we can safely write to (with the amount we want write subtracted before we write)
516 char *writeStartOffset = finishEndOfDataOffset;
517
518 //printf("\n******* MOVEDATABEYOND *******\n");
519 //printf("Space start: %d, space end: %d\n", spaceStartOffset - (char *)pvSaveMem, spaceEndOffset - (char *)pvSaveMem);
520 //printf("Current end of data: %d, new end of data: %d\n", beginEndOfDataOffset - (char *)pvSaveMem, finishEndOfDataOffset - (char *)pvSaveMem);
521
522 // 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
523 // of move and code explicitly for this situation
524 if( ( nNumberOfBytesToWrite & 4095 ) == 0 )
525 {
526 if( nNumberOfBytesToWrite > 0 )
527 {
528 // Get addresses for start & end of the region we are copying from as uintptr_t, for easier maths
529 uintptr_t uiFromStart = (uintptr_t)spaceStartOffset;
530 uintptr_t uiFromEnd = (uintptr_t)beginEndOfDataOffset;
531
532 // Round both of these values to get 4096 byte chunks that we will need to at least partially move
533 uintptr_t uiFromStartChunk = uiFromStart & ~((uintptr_t)4095);
534 uintptr_t uiFromEndChunk = (uiFromEnd - 1 ) & ~((uintptr_t)4095);
535
536 // Loop through all the affected source 4096 chunks, going backwards so we don't overwrite anything we'll need in the future
537 for( uintptr_t uiCurrentChunk = uiFromEndChunk; uiCurrentChunk >= uiFromStartChunk; uiCurrentChunk -= 4096 )
538 {
539 // Establish chunk we'll need to copy
540 uintptr_t uiCopyStart = uiCurrentChunk;
541 uintptr_t uiCopyEnd = uiCurrentChunk + 4096;
542 // Clamp chunk to the bounds of the full region we are trying to copy
543 if( uiCopyStart < uiFromStart )
544 {
545 // Needs to be clampged against the start of our region
546 uiCopyStart = uiFromStart;
547 }
548 if ( uiCopyEnd > uiFromEnd )
549 {
550 // Needs to be clamped to the end of our region
551 uiCopyEnd = uiFromEnd;
552 }
553#ifdef __PSVITA__
554 // AP - use this to access the virtual memory
555 VirtualMove( (void *)(uiCopyStart + nNumberOfBytesToWrite), ( void *)uiCopyStart, uiCopyEnd - uiCopyStart);
556#else
557 XMemCpy( (void *)(uiCopyStart + nNumberOfBytesToWrite), ( void *)uiCopyStart, uiCopyEnd - uiCopyStart );
558#endif
559 }
560 }
561 }
562 else
563 {
564 while(true)
565 {
566 // Copy buffer 1 to buffer 2
567 memcpy( buffer2, buffer1, buffer1Size);
568 buffer2Size = buffer1Size;
569
570 // Fill buffer 1 from file
571 if( (readStartOffset - bufferSize) < spaceStartOffset )
572 {
573 amountToRead = (DWORD)(readStartOffset - spaceStartOffset);
574 }
575 else
576 {
577 amountToRead = bufferSize;
578 }
579
580 // Push the read point back by the amount of bytes that we are going to read
581 readStartOffset -= amountToRead;
582
583 //printf("About to read %u from %d\n", amountToRead, readStartOffset - (char *)pvSaveMem );
584#ifdef __PSVITA__
585 // AP - use this to access the virtual memory
586 VirtualCopyFrom(buffer1, readStartOffset, amountToRead);
587#else
588 memcpy( buffer1, readStartOffset, amountToRead );
589#endif
590 numberOfBytesRead = amountToRead;
591
592 buffer1Size = amountToRead;
593
594 // Move back the write pointer by the amount of bytes we are going to write
595 writeStartOffset -= buffer2Size;
596
597 // Write buffer 2 to file
598 if( (writeStartOffset + buffer2Size) <= finishEndOfDataOffset)
599 {
600 //printf("About to write %u to %d\n", buffer2Size, writeStartOffset - (char *)pvSaveMem );
601#ifdef __PSVITA__
602 // AP - use this to access the virtual memory
603 VirtualCopyTo((void *)writeStartOffset, buffer2, buffer2Size);
604#else
605 memcpy( (void *)writeStartOffset, buffer2, buffer2Size );
606#endif
607 numberOfBytesWritten = buffer2Size;
608 }
609 else
610 {
611 assert((writeStartOffset + buffer2Size) <= finishEndOfDataOffset);
612 numberOfBytesWritten = 0;
613 }
614
615 if( numberOfBytesRead == 0 )
616 {
617 //printf("\n************** MOVE COMPLETED *************** \n\n");
618 assert( writeStartOffset == spaceEndOffset );
619 break;
620 }
621 }
622 }
623
624 header.AdjustStartOffsets( file, nNumberOfBytesToWrite );
625}
626
627bool ConsoleSaveFileOriginal::doesFileExist(ConsoleSavePath file)
628{
629 LockSaveAccess();
630 bool exists = header.fileExists( file.getName() );
631 ReleaseSaveAccess();
632
633 return exists;
634}
635
636void ConsoleSaveFileOriginal::Flush(bool autosave, bool updateThumbnail )
637{
638 LockSaveAccess();
639
640#ifdef __PSVITA__
641 // On Vita we've had problems with saves being corrupted on rapid save/save-exiting so seems prudent to wait for idle
642 while( StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle )
643 {
644 app.DebugPrintf("Flush wait\n");
645 Sleep(10);
646 }
647#endif
648
649 finalizeWrite();
650
651 // Get the frequency of the timer
652 LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime;
653 float fElapsedTime = 0.0f;
654 QueryPerformanceFrequency( &qwTicksPerSec );
655 float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart;
656
657 unsigned int fileSize = header.GetFileSize();
658
659 // Assume that the compression will make it smaller so initially attempt to allocate the current file size
660 // We add 4 bytes to the start so that we can signal compressed data
661 // And another 4 bytes to store the decompressed data size
662 unsigned int compLength = fileSize+8;
663
664 // 4J Stu - Added TU-1 interim
665
666#ifdef __PS3__
667 // On PS3, don't compress the data as we can't really afford the extra memory this requires for the output buffer. Instead we'll be writing
668 // directly from the save data.
669 StorageManager.SetSaveData(pvSaveMem,fileSize);
670 byte *compData = (byte *)pvSaveMem;
671#else
672 // Attempt to allocate the required memory
673 // We do not own this, it belongs to the StorageManager
674 byte *compData = (byte *)StorageManager.AllocateSaveData( compLength );
675
676#ifdef __PSVITA__
677 // AP - make sure we always allocate just what is needed so it will only SAVE what is needed.
678 // If we don't do this the StorageManager will save a file of uncompressed size unnecessarily.
679 compData = NULL;
680#endif
681
682 // If we failed to allocate then compData will be NULL
683 // Pre-calculate the compressed data size so that we can attempt to allocate a smaller buffer
684 if(compData == NULL)
685 {
686 // Length should be 0 here so that the compression call knows that we want to know the length back
687 compLength = 0;
688
689 // Pre-calculate the buffer size required for the compressed data
690 PIXBeginNamedEvent(0,"Pre-calc save compression");
691 // Save the start time
692 QueryPerformanceCounter( &qwTime );
693#ifdef __PSVITA__
694 // AP - get the compressed size via the access function. This uses a special RLE format
695 VirtualCompress(NULL,&compLength,pvSaveMem,fileSize);
696#else
697 Compression::getCompression()->Compress(NULL,&compLength,pvSaveMem,fileSize);
698#endif
699 QueryPerformanceCounter( &qwNewTime );
700
701 qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
702 fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart));
703
704 app.DebugPrintf("Check buffer size: Elapsed time %f\n", fElapsedTime);
705 PIXEndNamedEvent();
706
707 // We add 4 bytes to the start so that we can signal compressed data
708 // And another 4 bytes to store the decompressed data size
709 compLength = compLength+8;
710
711 // Attempt to allocate the required memory
712 compData = (byte *)StorageManager.AllocateSaveData( compLength );
713 }
714#endif
715
716 if(compData != NULL)
717 {
718 // No compression on PS3 - see comment above
719#ifndef __PS3__
720 // Re-compress all save data before we save it to disk
721 PIXBeginNamedEvent(0,"Actual save compression");
722 // Save the start time
723 QueryPerformanceCounter( &qwTime );
724#ifdef __PSVITA__
725 // AP - compress via the access function. This uses a special RLE format
726 VirtualCompress(compData+8,&compLength,pvSaveMem,fileSize);
727#else
728 Compression::getCompression()->Compress(compData+8,&compLength,pvSaveMem,fileSize);
729#endif
730 QueryPerformanceCounter( &qwNewTime );
731
732 qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
733 fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart));
734
735 app.DebugPrintf("Compress: Elapsed time %f\n", fElapsedTime);
736 PIXEndNamedEvent();
737
738 ZeroMemory(compData,8);
739 int saveVer = 0;
740 memcpy( compData, &saveVer, sizeof(int) );
741 memcpy( compData+4, &fileSize, sizeof(int) );
742
743 app.DebugPrintf("Save data compressed from %d to %d\n", fileSize, compLength);
744#endif
745
746 PBYTE pbThumbnailData=NULL;
747 DWORD dwThumbnailDataSize=0;
748
749 PBYTE pbDataSaveImage=NULL;
750 DWORD dwDataSizeSaveImage=0;
751
752#if ( defined _XBOX || defined _DURANGO )
753 app.GetSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize);
754#elif ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ )
755 app.GetSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize,&pbDataSaveImage,&dwDataSizeSaveImage);
756#endif
757
758 BYTE bTextMetadata[88];
759 ZeroMemory(bTextMetadata,88);
760
761 __int64 seed = 0;
762 bool hasSeed = false;
763 if(MinecraftServer::getInstance()!= NULL && MinecraftServer::getInstance()->levels[0]!=NULL)
764 {
765 seed = MinecraftServer::getInstance()->levels[0]->getLevelData()->getSeed();
766 hasSeed = true;
767 }
768
769 int iTextMetadataBytes = app.CreateImageTextData(bTextMetadata, seed, hasSeed, app.GetGameHostOption(eGameHostOption_All), Minecraft::GetInstance()->getCurrentTexturePackId());
770
771 INT saveOrCheckpointId = 0;
772 bool validSave = StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId);
773 TelemetryManager->RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), saveOrCheckpointId, compLength+8);
774
775#ifdef _XBOX
776 StorageManager.SaveSaveData( compLength+8,pbThumbnailData,dwThumbnailDataSize,bTextMetadata,iTextMetadataBytes );
777 delete [] pbThumbnailData;
778#ifndef _CONTENT_PACKAGE
779 if( app.DebugSettingsOn())
780 {
781 if(app.GetWriteSavesToFolderEnabled() )
782 {
783 DebugFlushToFile(compData, compLength+8);
784 }
785 }
786#endif
787 }
788 else
789 {
790 // We have failed to allocate the memory required to save this file. Now what?
791 }
792
793 ReleaseSaveAccess();
794#elif (defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ || defined _DURANGO || defined _WINDOWS64)
795 // set the icon and save image
796 StorageManager.SetSaveImages(pbThumbnailData,dwThumbnailDataSize,pbDataSaveImage,dwDataSizeSaveImage,bTextMetadata,iTextMetadataBytes);
797 app.DebugPrintf("Save thumbnail size %d\n",dwThumbnailDataSize);
798
799 // save the data
800 StorageManager.SaveSaveData( &ConsoleSaveFileOriginal::SaveSaveDataCallback, this );
801#ifndef _CONTENT_PACKAGE
802 if( app.DebugSettingsOn())
803 {
804 if(app.GetWriteSavesToFolderEnabled() )
805 {
806 DebugFlushToFile(compData, compLength+8);
807 }
808 }
809#endif
810 ReleaseSaveAccess();
811 }
812#else
813 }
814 ReleaseSaveAccess();
815#endif
816}
817
818#if (defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ || defined _DURANGO || defined _WINDOWS64)
819
820int ConsoleSaveFileOriginal::SaveSaveDataCallback(LPVOID lpParam,bool bRes)
821{
822 ConsoleSaveFile *pClass=(ConsoleSaveFile *)lpParam;
823
824 return 0;
825}
826
827#endif
828
829#ifndef _CONTENT_PACKAGE
830void ConsoleSaveFileOriginal::DebugFlushToFile(void *compressedData /*= NULL*/, unsigned int compressedDataSize /*= 0*/)
831{
832 LockSaveAccess();
833
834 finalizeWrite();
835
836 unsigned int fileSize = header.GetFileSize();
837
838 DWORD numberOfBytesWritten = 0;
839#ifdef _XBOX
840 File targetFileDir(L"GAME:\\Saves");
841#else
842 File targetFileDir(L"Saves");
843#endif // _XBOX
844
845 if(!targetFileDir.exists())
846 targetFileDir.mkdir();
847
848 wchar_t *fileName = new wchar_t[XCONTENT_MAX_FILENAME_LENGTH+1];
849
850 SYSTEMTIME t;
851 GetSystemTime( &t );
852
853 //14 chars for the digits
854 //11 chars for the separators + suffix
855 //25 chars total
856 wstring cutFileName = m_fileName;
857 if(m_fileName.length() > XCONTENT_MAX_FILENAME_LENGTH - 25)
858 {
859 cutFileName = m_fileName.substr(0, XCONTENT_MAX_FILENAME_LENGTH - 25);
860 }
861 swprintf(fileName, XCONTENT_MAX_FILENAME_LENGTH+1, L"\\v%04d-%ls%02d.%02d.%02d.%02d.%02d.mcs",VER_PRODUCTBUILD,cutFileName.c_str(), t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);
862
863#ifdef _UNICODE
864 wstring wtemp = targetFileDir.getPath() + wstring(fileName);
865 LPCWSTR lpFileName = wtemp.c_str();
866#else
867 LPCSTR lpFileName = wstringtofilename( targetFileDir.getPath() + wstring(fileName) );
868#endif
869#ifndef __PSVITA__
870 HANDLE hSaveFile = CreateFile( lpFileName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, NULL);
871#endif
872
873 if(compressedData != NULL && compressedDataSize > 0)
874 {
875#ifdef __PSVITA__
876 // AP - Use the access function to save
877 VirtualWriteFile( lpFileName, compressedData, compressedDataSize, &numberOfBytesWritten, NULL);
878#else
879 WriteFile( hSaveFile,compressedData,compressedDataSize,&numberOfBytesWritten,NULL);
880#endif
881 assert(numberOfBytesWritten == compressedDataSize);
882 }
883 else
884 {
885#ifdef __PSVITA__
886 // AP - Use the access function to save
887 VirtualWriteFile( lpFileName, compressedData, compressedDataSize, &numberOfBytesWritten, NULL);
888#else
889 WriteFile(hSaveFile,pvSaveMem,fileSize,&numberOfBytesWritten,NULL);
890#endif
891 assert(numberOfBytesWritten == fileSize);
892 }
893#ifndef __PSVITA__
894 CloseHandle( hSaveFile );
895#endif
896
897 delete[] fileName;
898
899 ReleaseSaveAccess();
900}
901#endif
902
903unsigned int ConsoleSaveFileOriginal::getSizeOnDisk()
904{
905 return header.GetFileSize();
906}
907
908wstring ConsoleSaveFileOriginal::getFilename()
909{
910 return m_fileName;
911}
912
913vector<FileEntry *> *ConsoleSaveFileOriginal::getFilesWithPrefix(const wstring &prefix)
914{
915 return header.getFilesWithPrefix( prefix );
916}
917
918vector<FileEntry *> *ConsoleSaveFileOriginal::getRegionFilesByDimension(unsigned int dimensionIndex)
919{
920 return NULL;
921}
922
923#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__)
924wstring ConsoleSaveFileOriginal::getPlayerDataFilenameForLoad(const PlayerUID& pUID)
925{
926 return header.getPlayerDataFilenameForLoad( pUID );
927}
928wstring ConsoleSaveFileOriginal::getPlayerDataFilenameForSave(const PlayerUID& pUID)
929{
930 return header.getPlayerDataFilenameForSave( pUID );
931}
932vector<FileEntry *> *ConsoleSaveFileOriginal::getValidPlayerDatFiles()
933{
934 return header.getValidPlayerDatFiles();
935}
936#endif
937
938int ConsoleSaveFileOriginal::getSaveVersion()
939{
940 return header.getSaveVersion();
941}
942
943int ConsoleSaveFileOriginal::getOriginalSaveVersion()
944{
945 return header.getOriginalSaveVersion();
946}
947
948void ConsoleSaveFileOriginal::LockSaveAccess()
949{
950 EnterCriticalSection(&m_lock);
951}
952
953void ConsoleSaveFileOriginal::ReleaseSaveAccess()
954{
955 LeaveCriticalSection(&m_lock);
956}
957
958ESavePlatform ConsoleSaveFileOriginal::getSavePlatform()
959{
960 return header.getSavePlatform();
961}
962
963bool ConsoleSaveFileOriginal::isSaveEndianDifferent()
964{
965 return header.isSaveEndianDifferent();
966}
967
968void ConsoleSaveFileOriginal::setLocalPlatform()
969{
970 header.setLocalPlatform();
971}
972
973void ConsoleSaveFileOriginal::setPlatform(ESavePlatform plat)
974{
975 header.setPlatform(plat);
976}
977
978ByteOrder ConsoleSaveFileOriginal::getSaveEndian()
979{
980 return header.getSaveEndian();
981}
982
983ByteOrder ConsoleSaveFileOriginal::getLocalEndian()
984{
985 return header.getLocalEndian();
986}
987
988void ConsoleSaveFileOriginal::setEndian(ByteOrder endian)
989{
990 header.setEndian(endian);
991}
992
993bool ConsoleSaveFileOriginal::isLocalEndianDifferent( ESavePlatform plat )
994{
995 return getLocalEndian() != header.getEndian(plat);
996}
997
998
999void ConsoleSaveFileOriginal::ConvertRegionFile(File sourceFile)
1000{
1001 DWORD numberOfBytesWritten = 0;
1002 DWORD numberOfBytesRead = 0;
1003
1004 RegionFile sourceRegionFile(this, &sourceFile);
1005
1006 for(unsigned int x = 0; x < 32; ++x)
1007 {
1008 for(unsigned int z = 0; z < 32; ++z)
1009 {
1010 DataInputStream *dis = sourceRegionFile.getChunkDataInputStream(x,z);
1011
1012 if(dis)
1013 {
1014 byteArray inData(1024*1024);
1015 int read = dis->read(inData);
1016 dis->close();
1017 dis->deleteChildStream();
1018 delete dis;
1019
1020 DataOutputStream *dos = sourceRegionFile.getChunkDataOutputStream(x,z);
1021 dos->write(inData, 0, read);
1022
1023
1024 dos->close();
1025 dos->deleteChildStream();
1026 delete dos;
1027 delete inData.data;
1028
1029 }
1030
1031 }
1032 }
1033 sourceRegionFile.writeAllOffsets(); // saves all the endian swapped offsets back out to the file (not all of these are written in the above processing).
1034
1035}
1036
1037void ConsoleSaveFileOriginal::ConvertToLocalPlatform()
1038{
1039 if(getSavePlatform() == SAVE_FILE_PLATFORM_LOCAL)
1040 {
1041 // already in the correct format
1042 return;
1043 }
1044 // convert each of the region files to the local platform
1045 vector<FileEntry *> *allFilesInSave = getFilesWithPrefix(wstring(L""));
1046 for(AUTO_VAR(it, allFilesInSave->begin()); it < allFilesInSave->end(); ++it)
1047 {
1048 FileEntry *fe = *it;
1049 wstring fName( fe->data.filename );
1050 wstring suffix(L".mcr");
1051 if( fName.compare(fName.length() - suffix.length(), suffix.length(), suffix) == 0 )
1052 {
1053 app.DebugPrintf("Processing a region file: %ls\n",fName.c_str());
1054 ConvertRegionFile(File(fe->data.filename) );
1055 }
1056 else
1057 {
1058 app.DebugPrintf("%ls is not a region file, ignoring\n", fName.c_str());
1059 }
1060 }
1061
1062 setLocalPlatform(); // set the platform of this save to the local platform, now that it's been coverted
1063}
1064
1065void *ConsoleSaveFileOriginal::getWritePointer(FileEntry *file)
1066{
1067 return (char *)pvSaveMem + file->currentFilePointer;;
1068}