the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 1068 lines 32 kB view raw
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}