Something went wrong. Try again.
the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
Something went wrong. Try again.
19 kB · 682 lines
C++
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682#include "stdafx.h"#include "FileHeader.h"
//#define _DEBUG_FILE_HEADER
FileHeader::FileHeader(){ lastFile = NULL; m_saveVersion = 0;
// New saves should have an original version set to the latest version. This will be overridden when we load a save m_originalSaveVersion = SAVE_FILE_VERSION_NUMBER; m_savePlatform = SAVE_FILE_PLATFORM_LOCAL; m_saveEndian = m_localEndian;}
FileHeader::~FileHeader(){ for( unsigned int i = 0; i < fileTable.size(); ++i ) { delete fileTable[i]; }}
FileEntry *FileHeader::AddFile( const wstring &name, unsigned int length /* = 0 */ ){ assert( name.length() < 64 ); wchar_t filename[64]; memset( &filename, 0, sizeof( wchar_t ) * 64 ); memcpy( &filename, name.c_str(), min( sizeof( wchar_t ) * 64, sizeof( wchar_t ) * name.length() ) );
// Would a map be more efficient? Our file tables probably won't be very big so better to avoid hashing all the time? // Does the file exist? for( unsigned int i = 0; i < fileTable.size(); ++i ) { if( wcscmp( fileTable[i]->data.filename, filename ) == 0 ) { // If so, return it return fileTable[i]; } }
// Else, add it to our file table fileTable.push_back( new FileEntry( filename, length, GetStartOfNextData() ) ); lastFile = fileTable[ fileTable.size() - 1 ]; return lastFile;}
void FileHeader::RemoveFile( FileEntry *file ){ if( file == NULL ) return;
AdjustStartOffsets(file, file->getFileSize(), true);
AUTO_VAR(it, find(fileTable.begin(), fileTable.end(),file));
if( it < fileTable.end() ) { fileTable.erase( it ); }
#ifndef _CONTENT_PACKAGE wprintf(L"Removed file %ls\n", file->data.filename);#endif delete file;}
void FileHeader::WriteHeader( LPVOID saveMem ){ unsigned int headerOffset = GetStartOfNextData();
// 4J Changed for save version 2 to be the number of files rather than the size in bytes unsigned int headerSize = (int)(fileTable.size()); //DWORD numberOfBytesWritten = 0;
// Write the offset of the header //assert(numberOfBytesWritten == 4); int *begin = (int *)saveMem;#ifdef __PSVITA__ VirtualCopyTo(begin, &headerOffset, sizeof(headerOffset));#else *begin = headerOffset;#endif
// Write the size of the header //assert(numberOfBytesWritten == 4); #ifdef __PSVITA__ VirtualCopyTo(begin + 1, &headerSize, sizeof(headerSize));#else *(begin + 1) = headerSize;#endif
short *versions = (short *)(begin + 2); // Write the original version number#ifdef __PSVITA__ VirtualCopyTo(versions, &m_originalSaveVersion, sizeof(m_originalSaveVersion));#else *versions = m_originalSaveVersion;#endif
// Write the version number short versionNumber = SAVE_FILE_VERSION_NUMBER; //assert(numberOfBytesWritten == 4); //*(begin + 2) = versionNumber;#ifdef __PSVITA__ VirtualCopyTo(versions + 1, &versionNumber, sizeof(versionNumber));#else *(versions + 1) = versionNumber;#endif
#ifdef _DEBUG_FILE_HEADER app.DebugPrintf("Write save file with original version: %d, and current version %d\n", m_originalSaveVersion, versionNumber);#endif
char *headerPosition = (char *)saveMem + headerOffset;
#ifdef _DEBUG_FILE_HEADER app.DebugPrintf("\n\nWrite file Header: Offset = %d, Size = %d\n", headerOffset, headerSize);#endif
// Write the header for( unsigned int i = 0; i < fileTable.size(); ++i ) { //wprintf(L"File: %ls, Start = %d, Length = %d, End = %d\n", fileTable[i]->data.filename, fileTable[i]->data.startOffset, fileTable[i]->data.length, fileTable[i]->data.startOffset + fileTable[i]->data.length);#ifdef __PSVITA__ VirtualCopyTo((void *)headerPosition, &fileTable[i]->data, sizeof(FileEntrySaveData));#else memcpy( (void *)headerPosition, &fileTable[i]->data, sizeof(FileEntrySaveData) );#endif //assert(numberOfBytesWritten == sizeof(FileEntrySaveData)); headerPosition += sizeof(FileEntrySaveData); }}
void FileHeader::ReadHeader( LPVOID saveMem, ESavePlatform plat /*= SAVE_FILE_PLATFORM_LOCAL */ ){ unsigned int headerOffset; unsigned int headerSize;
m_savePlatform = plat;
switch(m_savePlatform) { case SAVE_FILE_PLATFORM_X360: case SAVE_FILE_PLATFORM_PS3: m_saveEndian = BIGENDIAN; break; case SAVE_FILE_PLATFORM_XBONE: case SAVE_FILE_PLATFORM_WIN64: case SAVE_FILE_PLATFORM_PS4: case SAVE_FILE_PLATFORM_PSVITA: m_saveEndian = LITTLEENDIAN; break; default: assert(0); m_savePlatform = SAVE_FILE_PLATFORM_LOCAL; m_saveEndian = m_localEndian; break; }
// Read the offset of the header //assert(numberOfBytesRead == 4); int *begin = (int *)saveMem;#ifdef __PSVITA__ VirtualCopyFrom(&headerOffset, begin, sizeof(headerOffset));#else headerOffset = *begin;#endif if(isSaveEndianDifferent()) System::ReverseULONG(&headerOffset);
// Read the size of the header //assert(numberOfBytesRead == 4);#ifdef __PSVITA__ VirtualCopyFrom(&headerSize, begin + 1, sizeof(headerSize));#else headerSize = *(begin + 1);#endif if(isSaveEndianDifferent()) System::ReverseULONG(&headerSize);
short *versions = (short *)(begin + 2); // Read the original save version number#ifdef __PSVITA__ VirtualCopyFrom(&m_originalSaveVersion, versions, sizeof(m_originalSaveVersion));#else m_originalSaveVersion = *(versions);#endif if(isSaveEndianDifferent()) System::ReverseSHORT(&m_originalSaveVersion);
// Read the save version number //m_saveVersion = *(begin + 2);#ifdef __PSVITA__ VirtualCopyFrom(&m_saveVersion, versions + 1, sizeof(m_saveVersion));#else m_saveVersion = *(versions + 1);#endif if(isSaveEndianDifferent()) System::ReverseSHORT(&m_saveVersion);
#ifdef _DEBUG_FILE_HEADER app.DebugPrintf("Read save file with orignal version: %d, and current version %d\n", m_originalSaveVersion, m_saveVersion); app.DebugPrintf("\n\nRead file Header: Offset = %d, Size = %d\n", headerOffset, headerSize);#endif
char *headerPosition = (char *)saveMem + headerOffset;
switch( m_saveVersion ) { //case SAVE_FILE_VERSION_NUMBER: //case 8: // 4J Stu - SAVE_FILE_VERSION_NUMBER 2,3,4,5,6,7,8 are the same, but: // : Bumped it to 3 in TU5 to force older builds (ie 0062) to generate a new world when trying to load new saves // : Bumped it to 4 in TU9 to delete versions of The End that were generated in builds prior to TU9 // : Bumped it to 5 in TU9 to update the map data that was only using 1 bit to determine dimension // : Bumped it to 6 for PS3 v1 to update map data mappings to use larger PlayerUID // : Bumped it to 7 for Durango v1 to update map data mappings to use string based PlayerUID // : Bumped it to 8 for Durango v1 when to save the chunks in a different compressed format case SAVE_FILE_VERSION_CHUNK_INHABITED_TIME: case SAVE_FILE_VERSION_COMPRESSED_CHUNK_STORAGE: case SAVE_FILE_VERSION_DURANGO_CHANGE_MAP_DATA_MAPPING_SIZE: case SAVE_FILE_VERSION_CHANGE_MAP_DATA_MAPPING_SIZE: case SAVE_FILE_VERSION_MOVED_STRONGHOLD: case SAVE_FILE_VERSION_NEW_END: case SAVE_FILE_VERSION_POST_LAUNCH: case SAVE_FILE_VERSION_LAUNCH: { // Changes for save file version 2: // headerSize is now a count of elements rather than a count of bytes // The FileEntrySaveData struct has a lastModifiedTime member
// Read the header FileEntrySaveData *fesdHeaderPosition = (FileEntrySaveData *)headerPosition; for(unsigned int i = 0; i < headerSize; ++i) { FileEntry *entry = new FileEntry(); //assert(numberOfBytesRead == sizeof(FileEntrySaveData)); #ifdef __PSVITA__ VirtualCopyFrom( &entry->data, fesdHeaderPosition, sizeof(FileEntrySaveData) );#else memcpy( &entry->data, fesdHeaderPosition, sizeof(FileEntrySaveData) );#endif
if(isSaveEndianDifferent()) { // Reverse bytes System::ReverseWCHARA(entry->data.filename,64); System::ReverseULONG(&entry->data.length); System::ReverseULONG(&entry->data.startOffset); System::ReverseULONGLONG(&entry->data.lastModifiedTime); }
entry->currentFilePointer = entry->data.startOffset; lastFile = entry; fileTable.push_back( entry );#ifdef _DEBUG_FILE_HEADER app.DebugPrintf("File: %ls, Start = %d, Length = %d, End = %d, Timestamp = %lld\n", entry->data.filename, entry->data.startOffset, entry->data.length, entry->data.startOffset + entry->data.length, entry->data.lastModifiedTime);#endif
fesdHeaderPosition++; } } break;
// Legacy save versions, with updated code to convert the FileEntrySaveData to the latest version // 4J Stu - At time of writing, the tutorial save is V1 so need to keep this for compatibility case SAVE_FILE_VERSION_PRE_LAUNCH: { // Read the header // We can then make headerPosition a FileEntrySaveData pointer and just increment by one up to the number unsigned int i = 0; while( i < headerSize ) { FileEntry *entry = new FileEntry(); //assert(numberOfBytesRead == sizeof(FileEntrySaveData));
#ifdef __PSVITA__ VirtualCopyFrom( &entry->data, headerPosition, sizeof(FileEntrySaveDataV1) );#else memcpy( &entry->data, headerPosition, sizeof(FileEntrySaveDataV1) );#endif
entry->currentFilePointer = entry->data.startOffset; lastFile = entry; fileTable.push_back( entry );#ifdef _DEBUG_FILE_HEADER app.DebugPrintf("File: %ls, Start = %d, Length = %d, End = %d\n", entry->data.filename, entry->data.startOffset, entry->data.length, entry->data.startOffset + entry->data.length);#endif
i += sizeof(FileEntrySaveDataV1); headerPosition += sizeof(FileEntrySaveDataV1); } } break; default:#ifndef _CONTENT_PACKAGE app.DebugPrintf("********** Invalid save version %d\n",m_saveVersion); __debugbreak();#endif break; }}
unsigned int FileHeader::GetStartOfNextData(){ // The first 4 bytes is the location of the header (the header itself is at the end of the file) // Then 4 bytes for the size of the header // Then 2 bytes for the version number at which this save was first generated // Then 2 bytes for the version number that the save should now be at unsigned int totalBytesSoFar = SAVE_FILE_HEADER_SIZE; for( unsigned int i = 0; i < fileTable.size(); ++i ) { if( fileTable[i]->getFileSize() > 0 ) totalBytesSoFar += fileTable[i]->getFileSize(); } return totalBytesSoFar;}
unsigned int FileHeader::GetFileSize(){ return GetStartOfNextData() + ( sizeof(FileEntrySaveData) * (unsigned int)fileTable.size() );}
void FileHeader::AdjustStartOffsets(FileEntry *file, DWORD nNumberOfBytesToWrite, bool subtract /*= false*/){ bool found = false; for( unsigned int i = 0; i < fileTable.size(); ++i ) { if( found == true ) { if(subtract) { fileTable[i]->data.startOffset -= nNumberOfBytesToWrite; fileTable[i]->currentFilePointer -= nNumberOfBytesToWrite; } else { fileTable[i]->data.startOffset += nNumberOfBytesToWrite; fileTable[i]->currentFilePointer += nNumberOfBytesToWrite; } } else if( fileTable[i] == file ) { found = true; } }}
bool FileHeader::fileExists( const wstring &name ){ for( unsigned int i = 0; i < fileTable.size(); ++i ) { if( wcscmp( fileTable[i]->data.filename, name.c_str() ) == 0 ) { // If so, return it return true; } } return false;}
vector<FileEntry *> *FileHeader::getFilesWithPrefix(const wstring &prefix){ vector<FileEntry *> *files = NULL;
for( unsigned int i = 0; i < fileTable.size(); ++i ) { if( wcsncmp( fileTable[i]->data.filename, prefix.c_str(), prefix.size() ) == 0 ) { if( files == NULL ) { files = new vector<FileEntry *>(); }
files->push_back(fileTable[i]); } }
return files;}
#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__)
static bool isHexChar(wchar_t wc){ if(wc >= L'0' && wc <= L'9') return true; if(wc >= L'a' && wc <= L'f') return true; return false;}static bool isHexString(wchar_t* ws, int size){ for(int i=0;i<size;i++) { if(!isHexChar(ws[i])) return false; } return true;}static bool isDecimalChar(wchar_t wc){ if(wc >= L'0' && wc <= L'9') return true; return false;}static bool isDecimalString(wchar_t* ws, int size){ for(int i=0;i<size;i++) { if(!isDecimalChar(ws[i])) return false; } return true;}static wchar_t* findFilenameStart(wchar_t* str){ // find the last forward slash in the filename, and return the pointer to the following character wchar_t* filenameStart= str; int len = wcslen(str); for(int i=0;i<len;i++) if(str[i] == L'/') filenameStart = &str[i+1]; return filenameStart;}
wstring FileHeader::getPlayerDataFilenameForLoad(const PlayerUID& pUID){ wstring retVal = L""; vector<FileEntry*>* pFiles = getDatFilesWithOnlineID(pUID); if(!pFiles) { // we didn't find a matching online dat file, so look for an offline version pFiles = getDatFilesWithMacAndUserID(pUID); } if(!pFiles) { // and we didn't find an offline file, so check if we're the primary user, and grab the primary file if we are if(pUID.isPrimaryUser()) { pFiles= getDatFilesWithPrimaryUser(); } } if(pFiles) { // we've got something to load// assert(pFiles->size() == 1); retVal = pFiles->at(0)->data.filename; delete pFiles; } return retVal;}
wstring FileHeader::getPlayerDataFilenameForSave(const PlayerUID& pUID){ // check if we're online first if(pUID.isSignedIntoPSN() == false) { // OK, we're not online, see if we can find another data file with matching mac and userID vector<FileEntry*>* pFiles = getDatFilesWithMacAndUserID(pUID); if(pFiles) { // we've found a previous save, use the filename from it, as it might have the online part too// assert(pFiles->size() == 1); wstring retVal = pFiles->at(0)->data.filename; delete pFiles; return retVal; } }
// we're either online, or we can't find a previous save, so use the standard filename wstring retVal = pUID.toString() + L".dat"; return retVal;}
vector<FileEntry *> *FileHeader::getValidPlayerDatFiles(){ vector<FileEntry *> *files = NULL;
// find filenames that match this pattern // P_5e7ff8372ea9_00000004_Mark_4J
for( unsigned int i = 0; i < fileTable.size(); ++i ) { wchar_t* filenameOnly = findFilenameStart(fileTable[i]->data.filename);
int nameLen = wcslen(filenameOnly); if(nameLen <= 4) continue;
// make sure it's a ".dat" file if( wcsncmp( &filenameOnly[nameLen-4], L".dat", 4 ) != 0 ) continue; // make sure we start with "P_" or "N_" if( ( wcsncmp(&filenameOnly[0], L"P_", 2) != 0 ) && ( wcsncmp( &filenameOnly[0], L"N_", 2 ) != 0 ) ) continue; //check the next 12 chars are hex if(!isHexString(&filenameOnly[2], 12)) continue; // make sure character 14 is '_' if(filenameOnly[14] != L'_') continue; //check the next 8 chars are decimal if(!isDecimalString(&filenameOnly[15], 8)) continue;
// if we get here, it must be a valid filename if( files == NULL ) { files = new vector<FileEntry *>(); } files->push_back(fileTable[i]); }
return files;}
vector<FileEntry *> *FileHeader::getDatFilesWithOnlineID(const PlayerUID& pUID){ if(pUID.isSignedIntoPSN() == false) return NULL;
vector<FileEntry *>* datFiles = getValidPlayerDatFiles(); if(datFiles == NULL) return NULL;
// we're looking for the online name from the pUID in these types of filenames - // P_5e7ff8372ea9_00000004_Mark_4J wchar_t onlineIDW[64]; mbstowcs(onlineIDW, pUID.getOnlineID(), 64);
vector<FileEntry *> *files = NULL; int onlineIDSize = wcslen(onlineIDW); if(onlineIDSize == 0) return NULL;
wcscat(onlineIDW, L".dat");
#ifdef __ORBIS__ onlineIDSize = wcslen(onlineIDW);#else static const int onlineIDStart = 24; // 24 characters into the filename#endif
char tempStr[128]; for( unsigned int i = 0; i < datFiles->size(); ++i ) { wchar_t* filenameOnly = findFilenameStart(datFiles->at(i)->data.filename); wcstombs(tempStr,filenameOnly, 128); app.DebugPrintf("file : %s\n", tempStr);
#ifdef __ORBIS__ int onlineIDStart = wcslen(filenameOnly) - onlineIDSize; if(onlineIDStart > 0)#else if(wcslen(filenameOnly) > onlineIDStart)#endif { if(wcsncmp(&filenameOnly[onlineIDStart], onlineIDW, onlineIDSize) == 0) { if( files == NULL ) { files = new vector<FileEntry *>(); } files->push_back(datFiles->at(i)); } } } delete datFiles;
if(files) sort(files->begin(), files->end(), FileEntry::newestFirst ); return files;}
vector<FileEntry *> *FileHeader::getDatFilesWithMacAndUserID(const PlayerUID& pUID){
vector<FileEntry *>* datFiles = getValidPlayerDatFiles(); if(datFiles == NULL) return NULL;
// we're looking for the mac address and userIDfrom the pUID in these types of filenames - // P_5e7ff8372ea9_00000004_Mark_4J std::wstring macStr = pUID.macAddressStr(); std::wstring userStr = pUID.userIDStr(); const wchar_t* pMacStr = macStr.c_str(); const wchar_t* pUserStr = userStr.c_str();
vector<FileEntry *> *files = NULL; static const int macAddrStart = 2; // 2 characters into the filename static const int userIDStart = 15; // 15 characters into the filename
char tempStr[128]; for( unsigned int i = 0; i < datFiles->size(); ++i ) { wchar_t* filenameOnly = findFilenameStart(datFiles->at(i)->data.filename); wcstombs(tempStr,filenameOnly, 128); app.DebugPrintf("file : %s\n", tempStr);
// check the mac address matches if(wcsncmp(&filenameOnly[macAddrStart], pMacStr, macStr.size()) == 0) { // check the userID matches if(wcsncmp(&filenameOnly[userIDStart], pUserStr, userStr.size()) == 0) { if( files == NULL ) { files = new vector<FileEntry *>(); } files->push_back(datFiles->at(i)); } } } delete datFiles; if(files) sort(files->begin(), files->end(), FileEntry::newestFirst ); return files;}
vector<FileEntry *> *FileHeader::getDatFilesWithPrimaryUser(){
vector<FileEntry *>* datFiles = getValidPlayerDatFiles(); if(datFiles == NULL) return NULL;
// we're just looking for filenames starting with "P_" in these types of filenames - // P_5e7ff8372ea9_00000004_Mark_4J vector<FileEntry *> *files = NULL;
char tempStr[128]; for( unsigned int i = 0; i < datFiles->size(); ++i ) { wchar_t* filenameOnly = findFilenameStart(datFiles->at(i)->data.filename); wcstombs(tempStr,filenameOnly, 128); app.DebugPrintf("file : %s\n", tempStr);
// check for "P_" prefix if(wcsncmp(&filenameOnly[0], L"P_", 2) == 0) { if( files == NULL ) { files = new vector<FileEntry *>(); } files->push_back(datFiles->at(i)); } } delete datFiles; if(files) sort(files->begin(), files->end(), FileEntry::newestFirst ); return files;}#endif // __PS3__ || __ORBIS__
ByteOrder FileHeader::getEndian( ESavePlatform plat ){ ByteOrder platEndian; switch(plat) { case SAVE_FILE_PLATFORM_X360: case SAVE_FILE_PLATFORM_PS3: return BIGENDIAN; break;
case SAVE_FILE_PLATFORM_NONE: case SAVE_FILE_PLATFORM_XBONE: case SAVE_FILE_PLATFORM_PS4: case SAVE_FILE_PLATFORM_PSVITA: case SAVE_FILE_PLATFORM_WIN64: return LITTLEENDIAN; break; default: assert(0); break; } return LITTLEENDIAN;}