the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 497 lines 15 kB view raw
1#include "stdafx.h" 2#include "System.h" 3#include "InputOutputStream.h" 4#include "File.h" 5#include "RegionFile.h" 6 7#include "ConsoleSaveFile.h" 8 9byteArray RegionFile::emptySector(SECTOR_BYTES); 10 11RegionFile::RegionFile(ConsoleSaveFile *saveFile, File *path) 12{ 13 _lastModified = 0; 14 15 m_saveFile = saveFile; 16 17 offsets = new int[SECTOR_INTS]; 18 memset(offsets,0,SECTOR_BYTES); 19 chunkTimestamps = new int[SECTOR_INTS]; 20 memset(chunkTimestamps,0,SECTOR_BYTES); 21 22 /* 4J Jev, using files instead of strings: 23 strncpy(fileName,path,MAX_PATH_SIZE); */ 24 25 fileName = path; 26 27// debugln("REGION LOAD " + fileName); 28 29 sizeDelta = 0; 30 31 // 4J - removed try/catch 32// try { 33 34 /* 4J - Removed as _lastModifed not used and this is always failing as checking wrong thing 35 if( path->exists() ) 36 { 37 _lastModified = path->lastModified(); 38 } 39 */ 40 41 fileEntry = m_saveFile->createFile( fileName->getName() ); 42 m_saveFile->setFilePointer( fileEntry, 0, NULL, FILE_END ); 43 44 if ( fileEntry->getFileSize() < SECTOR_BYTES) 45 { 46 // 4J altered - the original code used to write out 2 empty sectors here, which we don't want to do as we might be at a point where we shouldn't be touching the save file. 47 // This now happens in insertInitialSectors when we do a first write to the region 48 m_bIsEmpty = true; 49 50 sizeDelta += SECTOR_BYTES * 2; 51 } 52 else 53 { 54 m_bIsEmpty = false; 55 } 56 57 //if ((GetFileSize(file,NULL) & 0xfff) != 0) 58 if ((fileEntry->getFileSize() & 0xfff) != 0) 59 { 60 //byte zero = 0; 61 DWORD numberOfBytesWritten = 0; 62 DWORD bytesToWrite = 0x1000 - (fileEntry->getFileSize() & 0xfff); 63 byte *zeroBytes = new byte[ bytesToWrite ]; 64 ZeroMemory(zeroBytes, bytesToWrite); 65 66 /* the file size is not a multiple of 4KB, grow it */ 67 m_saveFile->writeFile(fileEntry,zeroBytes,bytesToWrite,&numberOfBytesWritten); 68 69 delete [] zeroBytes; 70 } 71 72 /* set up the available sector map */ 73 74 int nSectors; 75 if( m_bIsEmpty ) // 4J - added this case for our empty files that we now don't create 76 { 77 nSectors = 2; 78 } 79 else 80 { 81 nSectors = (int) fileEntry->getFileSize() / SECTOR_BYTES; 82 } 83 sectorFree = new vector<bool>; 84 sectorFree->reserve(nSectors); 85 86 for (int i = 0; i < nSectors; ++i) 87 { 88 sectorFree->push_back(true); 89 } 90 91 sectorFree->at(0) = false; // chunk offset table 92 sectorFree->at(1) = false; // for the last modified info 93 94 m_saveFile->setFilePointer( fileEntry, 0, NULL, FILE_BEGIN ); 95 for (int i = 0; i < SECTOR_INTS; ++i) 96 { 97 unsigned int offset = 0; 98 DWORD numberOfBytesRead = 0; 99 if( !m_bIsEmpty ) // 4J added condition, don't read back if we've just created an empty file as we don't immediately write this anymore 100 { 101 m_saveFile->readFile(fileEntry, &offset, 4, &numberOfBytesRead); 102 103 if(saveFile->isSaveEndianDifferent()) System::ReverseULONG(&offset); 104 105 } 106 offsets[i] = offset; 107 if (offset != 0 && (offset >> 8) + (offset & 0xFF) <= sectorFree->size()) 108 { 109 for (unsigned int sectorNum = 0; sectorNum < (offset & 0xFF); ++sectorNum) 110 { 111 sectorFree->at((offset >> 8) + sectorNum) = false; 112 } 113 } 114 } 115 for (int i = 0; i < SECTOR_INTS; ++i) 116 { 117 int lastModValue = 0; 118 DWORD numberOfBytesRead = 0; 119 if( !m_bIsEmpty ) // 4J added condition, don't read back if we've just created an empty file as we don't immediately write this anymore 120 { 121 m_saveFile->readFile(fileEntry, &lastModValue, 4, &numberOfBytesRead); 122 123 if(saveFile->isSaveEndianDifferent()) System::ReverseINT(&lastModValue); 124 } 125 chunkTimestamps[i] = lastModValue; 126 } 127 128 129// } catch (IOException e) { 130// e.printStackTrace(); 131// } 132} 133 134void RegionFile::writeAllOffsets() // used for the file ConsoleSaveFile conversion between platforms 135{ 136 if(m_bIsEmpty == false) 137 { 138 // save all the offsets and timestamps 139 m_saveFile->LockSaveAccess(); 140 141 DWORD numberOfBytesWritten = 0; 142 m_saveFile->setFilePointer( fileEntry, 0, NULL, FILE_BEGIN ); 143 m_saveFile->writeFile(fileEntry,offsets, SECTOR_BYTES ,&numberOfBytesWritten); 144 145 numberOfBytesWritten = 0; 146 m_saveFile->setFilePointer( fileEntry, SECTOR_BYTES, NULL, FILE_BEGIN ); 147 m_saveFile->writeFile(fileEntry, chunkTimestamps, SECTOR_BYTES, &numberOfBytesWritten); 148 149 m_saveFile->ReleaseSaveAccess(); 150 } 151 152} 153RegionFile::~RegionFile() 154{ 155 delete[] offsets; 156 delete[] chunkTimestamps; 157 delete sectorFree; 158 m_saveFile->closeHandle( fileEntry ); 159} 160 161__int64 RegionFile::lastModified() 162{ 163 return _lastModified; 164} 165 166int RegionFile::getSizeDelta() // TODO - was synchronized 167{ 168 int ret = sizeDelta; 169 sizeDelta = 0; 170 return ret; 171} 172 173DataInputStream *RegionFile::getChunkDataInputStream(int x, int z) // TODO - was synchronized 174{ 175 if (outOfBounds(x, z)) 176 { 177// debugln("READ", x, z, "out of bounds"); 178 return NULL; 179 } 180 181 // 4J - removed try/catch 182// try { 183 int offset = getOffset(x, z); 184 if (offset == 0) 185 { 186 // debugln("READ", x, z, "miss"); 187 return NULL; 188 } 189 190 unsigned int sectorNumber = offset >> 8; 191 unsigned int numSectors = offset & 0xFF; 192 193 if (sectorNumber + numSectors > sectorFree->size()) 194 { 195// debugln("READ", x, z, "invalid sector"); 196 return NULL; 197 } 198 199 m_saveFile->LockSaveAccess(); 200 201 //SetFilePointer(file,sectorNumber * SECTOR_BYTES,0,FILE_BEGIN); 202 m_saveFile->setFilePointer( fileEntry, sectorNumber * SECTOR_BYTES, NULL, FILE_BEGIN); 203 204 unsigned int length; 205 unsigned int decompLength; 206 unsigned int readDecompLength; 207 208 DWORD numberOfBytesRead = 0; 209 210 // 4J - this differs a bit from the java file format. Java has length stored as an int, then a type as a byte, then length-1 bytes of data 211 // We store length and decompression length as ints, then length bytes of xbox LZX compressed data 212 m_saveFile->readFile(fileEntry,&length,4,&numberOfBytesRead); 213 214 if(m_saveFile->isSaveEndianDifferent()) System::ReverseULONG(&length); 215 216 // Using to bit of length to signify that this data was compressed with RLE method 217 bool useRLE = false; 218 if( length & 0x80000000 ) 219 { 220 useRLE = true; 221 length &= 0x7fffffff; 222 } 223 m_saveFile->readFile(fileEntry,&decompLength,4,&numberOfBytesRead); 224 225 if(m_saveFile->isSaveEndianDifferent()) System::ReverseULONG(&decompLength); 226 227 if (length > SECTOR_BYTES * numSectors) 228 { 229// debugln("READ", x, z, "invalid length: " + length + " > 4096 * " + numSectors); 230 231 m_saveFile->ReleaseSaveAccess(); 232 return NULL; 233 } 234 235 MemSect(50); 236 byte *data = new byte[length]; 237 byte *decomp = new byte[decompLength]; 238 MemSect(0); 239 readDecompLength = decompLength; 240 m_saveFile->readFile(fileEntry,data,length,&numberOfBytesRead); 241 242 m_saveFile->ReleaseSaveAccess(); 243 244 Compression::getCompression()->SetDecompressionType(m_saveFile->getSavePlatform()); // if this save is from another platform, set the correct decompression type 245 246 if( useRLE ) 247 { 248 Compression::getCompression()->DecompressLZXRLE(decomp, &readDecompLength, data, length ); 249 } 250 else 251 { 252 Compression::getCompression()->Decompress(decomp, &readDecompLength, data, length ); 253 } 254 255 Compression::getCompression()->SetDecompressionType(SAVE_FILE_PLATFORM_LOCAL); // and then set the decompression back to the local machine's standard type 256 257 delete [] data; 258 259 // 4J - was InflaterInputStream in here too, but we've already decompressed 260 DataInputStream *ret = new DataInputStream(new ByteArrayInputStream( byteArray( decomp, readDecompLength) )); 261 return ret; 262 263// } catch (IOException e) { 264// debugln("READ", x, z, "exception"); 265// return null; 266// } 267} 268 269DataOutputStream *RegionFile::getChunkDataOutputStream(int x, int z) 270{ 271 // 4J - was DeflatorOutputStream in here too, but we've already compressed 272 return new DataOutputStream( new ChunkBuffer(this, x, z)); 273} 274 275/* write a chunk at (x,z) with length bytes of data to disk */ 276void RegionFile::write(int x, int z, byte *data, int length) // TODO - was synchronized 277{ 278 // 4J Stu - Do the compression here so that we know how much space we need to store the compressed data 279 byte *compData = new byte[length + 2048]; // presuming compression is going to make this smaller... UPDATE - for some really small things this isn't the case. Added 2K on here to cover those. 280 unsigned int compLength = length; 281 Compression::getCompression()->CompressLZXRLE(compData,&compLength,data,length); 282 283 int sectorsNeeded = (compLength + CHUNK_HEADER_SIZE) / SECTOR_BYTES + 1; 284 285// app.DebugPrintf(">>>>>>>>>>>>>> writing compressed data for 0x%.8x, %d %d\n",fileEntry->data.regionIndex,x,z); 286 287 // maximum chunk size is 1MB 288 if (sectorsNeeded >= 256) 289 { 290 return; 291 } 292 293 m_saveFile->LockSaveAccess(); 294 { 295 int offset = getOffset(x, z); 296 int sectorNumber = offset >> 8; 297 int sectorsAllocated = offset & 0xFF; 298 299#ifndef _CONTENT_PACKAGE 300 if(sectorNumber < 0) 301 { 302 __debugbreak(); 303 } 304#endif 305 306 if (sectorNumber != 0 && sectorsAllocated == sectorsNeeded) 307 { 308 /* we can simply overwrite the old sectors */ 309 // debug("SAVE", x, z, length, "rewrite"); 310 #ifndef _CONTENT_PACKAGE 311 //wprintf(L"Writing chunk (%d,%d) in %ls from current sector %d to %d\n", x,z, fileEntry->data.filename, sectorNumber, sectorNumber + sectorsNeeded - 1); 312 #endif 313 write(sectorNumber, compData, length, compLength); 314 } 315 else 316 { 317 /* we need to allocate new sectors */ 318 319 /* mark the sectors previously used for this chunk as free */ 320 for (int i = 0; i < sectorsAllocated; ++i) 321 { 322 sectorFree->at(sectorNumber + i) = true; 323 } 324 // 4J added - zero this now unused region of the file, so it can be better compressed until it is reused 325 zero(sectorNumber, SECTOR_BYTES * sectorsAllocated); 326 327 PIXBeginNamedEvent(0,"Scanning for free space\n"); 328 /* scan for a free space large enough to store this chunk */ 329 int runStart = (int)(find(sectorFree->begin(),sectorFree->end(),true) - sectorFree->begin()); // 4J - was sectorFree.indexOf(true) 330 int runLength = 0; 331 if (runStart != -1) 332 { 333 for (unsigned int i = runStart; i < sectorFree->size(); ++i) 334 { 335 if (runLength != 0) 336 { 337 if (sectorFree->at(i)) runLength++; 338 else runLength = 0; 339 } else if (sectorFree->at(i)) 340 { 341 runStart = i; 342 runLength = 1; 343 } 344 if (runLength >= sectorsNeeded) 345 { 346 break; 347 } 348 } 349 } 350 PIXEndNamedEvent(); 351 352 if (runLength >= sectorsNeeded) 353 { 354 /* we found a free space large enough */ 355 // debug("SAVE", x, z, length, "reuse"); 356 sectorNumber = runStart; 357 setOffset(x, z, (sectorNumber << 8) | sectorsNeeded); 358 #ifndef _CONTENT_PACKAGE 359 //wprintf(L"Writing chunk (%d,%d) in %ls from old sector %d to %d\n", x,z, fileEntry->data.filename, sectorNumber, sectorNumber + sectorsNeeded - 1); 360 #endif 361 for (int i = 0; i < sectorsNeeded; ++i) 362 { 363 sectorFree->at(sectorNumber + i) = false; 364 } 365 write(sectorNumber, compData, length, compLength); 366 } 367 else 368 { 369 PIXBeginNamedEvent(0,"Expanding storage for %d sectors\n", sectorsNeeded); 370 /* 371 * no free space large enough found -- we need to grow the 372 * file 373 */ 374 // debug("SAVE", x, z, length, "grow"); 375 //SetFilePointer(file,0,0,FILE_END); 376 m_saveFile->setFilePointer( fileEntry, 0, NULL, FILE_END ); 377 378 sectorNumber = (int)sectorFree->size(); 379 #ifndef _CONTENT_PACAKGE 380 //wprintf(L"Writing chunk (%d,%d) in %ls from new sector %d to %d\n", x,z, fileEntry->data.filename, sectorNumber, sectorNumber + sectorsNeeded - 1); 381 #endif 382 DWORD numberOfBytesWritten = 0; 383 for (int i = 0; i < sectorsNeeded; ++i) 384 { 385 //WriteFile(file,emptySector.data,SECTOR_BYTES,&numberOfBytesWritten,NULL); 386 m_saveFile->writeFile(fileEntry,emptySector.data,SECTOR_BYTES,&numberOfBytesWritten); 387 sectorFree->push_back(false); 388 } 389 sizeDelta += SECTOR_BYTES * sectorsNeeded; 390 391 write(sectorNumber, compData, length, compLength); 392 setOffset(x, z, (sectorNumber << 8) | sectorsNeeded); 393 PIXEndNamedEvent(); 394 } 395 } 396 setTimestamp(x, z, (int) (System::currentTimeMillis() / 1000L)); 397 } 398 m_saveFile->ReleaseSaveAccess(); 399 400// } catch (IOException e) { 401// e.printStackTrace(); 402// } 403} 404 405/* write a chunk data to the region file at specified sector number */ 406void RegionFile::write(int sectorNumber, byte *data, int length, unsigned int compLength) 407{ 408 DWORD numberOfBytesWritten = 0; 409 //SetFilePointer(file,sectorNumber * SECTOR_BYTES,0,FILE_BEGIN); 410 m_saveFile->setFilePointer( fileEntry, sectorNumber * SECTOR_BYTES, NULL, FILE_BEGIN ); 411 412 // 4J - this differs a bit from the java file format. Java has length stored as an int, then a type as a byte, then length-1 bytes of data 413 // We store length and decompression length as ints, then length bytes of xbox LZX compressed data 414 415 // 4J Stu - We need to do the compression at a level above this, where it is checking for free space 416 417 compLength |= 0x80000000; // 4J - signify that this has been encoded with RLE method ( see code in getChunkDataInputStream() for matching detection of this) 418 m_saveFile->writeFile(fileEntry,&compLength,4,&numberOfBytesWritten); 419 compLength &= 0x7fffffff; 420 m_saveFile->writeFile(fileEntry,&length,4,&numberOfBytesWritten); 421 m_saveFile->writeFile(fileEntry,data,compLength,&numberOfBytesWritten); 422 delete[] data; 423} 424 425void RegionFile::zero(int sectorNumber, int length) 426{ 427 DWORD numberOfBytesWritten = 0; 428 //SetFilePointer(file,sectorNumber * SECTOR_BYTES,0,FILE_BEGIN); 429 m_saveFile->setFilePointer( fileEntry, sectorNumber * SECTOR_BYTES, NULL, FILE_BEGIN ); 430 m_saveFile->zeroFile( fileEntry, length, &numberOfBytesWritten ); 431} 432 433/* is this an invalid chunk coordinate? */ 434bool RegionFile::outOfBounds(int x, int z) 435{ 436 return x < 0 || x >= 32 || z < 0 || z >= 32; 437} 438 439int RegionFile::getOffset(int x, int z) 440{ 441 return offsets[x + z * 32]; 442} 443 444bool RegionFile::hasChunk(int x, int z) 445{ 446 return getOffset(x, z) != 0; 447} 448 449// 4J added - write the initial two sectors that used to be written in the ctor when the file was empty 450void RegionFile::insertInitialSectors() 451{ 452 m_saveFile->setFilePointer( fileEntry, 0, NULL, FILE_BEGIN ); 453 DWORD numberOfBytesWritten = 0; 454 byte zeroBytes[ SECTOR_BYTES ]; 455 ZeroMemory(zeroBytes, SECTOR_BYTES); 456 457 /* we need to write the chunk offset table */ 458 m_saveFile->writeFile(fileEntry,zeroBytes,SECTOR_BYTES,&numberOfBytesWritten); 459 460 // write another sector for the timestamp info 461 m_saveFile->writeFile(fileEntry,zeroBytes,SECTOR_BYTES,&numberOfBytesWritten); 462 463 m_bIsEmpty = false; 464} 465 466void RegionFile::setOffset(int x, int z, int offset) 467{ 468 if( m_bIsEmpty ) 469 { 470 insertInitialSectors(); // 4J added 471 } 472 473 DWORD numberOfBytesWritten = 0; 474 offsets[x + z * 32] = offset; 475 m_saveFile->setFilePointer( fileEntry, (x + z * 32) * 4, NULL, FILE_BEGIN ); 476 477 m_saveFile->writeFile(fileEntry,&offset,4,&numberOfBytesWritten); 478} 479 480void RegionFile::setTimestamp(int x, int z, int value) 481{ 482 if( m_bIsEmpty ) 483 { 484 insertInitialSectors(); // 4J added 485 } 486 487 DWORD numberOfBytesWritten = 0; 488 chunkTimestamps[x + z * 32] = value; 489 m_saveFile->setFilePointer( fileEntry, SECTOR_BYTES + (x + z * 32) * 4, NULL, FILE_BEGIN ); 490 491 m_saveFile->writeFile(fileEntry,&value,4,&numberOfBytesWritten); 492} 493 494void RegionFile::close() 495{ 496 m_saveFile->closeHandle( fileEntry ); 497}