diff --git a/Minecraft.Client/Textures/Packs/DLCTexturePack.cpp b/Minecraft.Client/Textures/Packs/DLCTexturePack.cpp index 3519b038c..b7df9e445 100644 --- a/Minecraft.Client/Textures/Packs/DLCTexturePack.cpp +++ b/Minecraft.Client/Textures/Packs/DLCTexturePack.cpp @@ -8,14 +8,47 @@ #include "../../Platform/Common/DLC/DLCTextureFile.h" #include "../../Platform/Common/DLC/DLCLocalisationFile.h" #include "../../../Minecraft.World/Util/StringHelpers.h" +#include "../../../Minecraft.World/Util/PortableFileIO.h" #include "../../Utils/StringTable.h" #include "../../Platform/Common/DLC/DLCAudioFile.h" +#include + #if defined _XBOX || defined _WINDOWS64 #include "../../Platform/Xbox/XML/ATGXmlParser.h" #include "../../Platform/Xbox/XML/xmlFilesCallback.h" #endif +namespace +{ + bool ReadPortableBinaryFile(File &file, PBYTE &data, DWORD &size) + { + const __int64 fileLength = file.length(); + if (fileLength < 0 || fileLength > static_cast<__int64>(std::numeric_limits::max())) + { + data = NULL; + size = 0; + return false; + } + + const std::size_t capacity = static_cast(fileLength); + PBYTE buffer = new BYTE[capacity == 0 ? 1 : capacity]; + const PortableFileIO::BinaryReadResult readResult = PortableFileIO::ReadBinaryFile(file.getPath(), buffer, capacity); + if (readResult.status != PortableFileIO::BinaryReadStatus::ok + || readResult.fileSize > std::numeric_limits::max()) + { + delete [] buffer; + data = NULL; + size = 0; + return false; + } + + data = buffer; + size = static_cast(readResult.fileSize); + return true; + } +} + DLCTexturePack::DLCTexturePack(DWORD id, DLCPack *pack, TexturePack *fallback) : AbstractTexturePack(id, NULL, pack->getName(), fallback) { m_dlcInfoPack = pack; @@ -305,30 +338,12 @@ int DLCTexturePack::packMounted(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicen if(xzpPath.exists()) { - const char *pchFilename=wstringtofilename(xzpPath.getPath()); - HANDLE fileHandle = CreateFile( - pchFilename, // file name - GENERIC_READ, // access mode - 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... - NULL, // Unused - OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it - FILE_FLAG_SEQUENTIAL_SCAN, // file attributes - NULL // Unsupported - ); - - if( fileHandle != INVALID_HANDLE_VALUE ) + PBYTE pbData = NULL; + DWORD bytesRead = 0; + if( ReadPortableBinaryFile(xzpPath, pbData, bytesRead) ) { - DWORD dwFileSize = xzpPath.length(); - DWORD bytesRead; - PBYTE pbData = (PBYTE) new BYTE[dwFileSize]; - BOOL success = ReadFile(fileHandle,pbData,dwFileSize,&bytesRead,NULL); - CloseHandle(fileHandle); - if(success) - { - DLCUIDataFile *uiDLCFile = (DLCUIDataFile *)texturePack->m_dlcDataPack->addFile(DLCManager::e_DLCType_UIData,L"TexturePack.xzp"); - uiDLCFile->addData(pbData,bytesRead,true); - - } + DLCUIDataFile *uiDLCFile = (DLCUIDataFile *)texturePack->m_dlcDataPack->addFile(DLCManager::e_DLCType_UIData,L"TexturePack.xzp"); + uiDLCFile->addData(pbData,bytesRead,true); } } #else @@ -354,43 +369,10 @@ int DLCTexturePack::packMounted(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicen File grf( getFilePath(texturePack->m_dlcInfoPack->GetPackID(), dlcFile->getGrfPath() ) ); if (grf.exists()) { -#if defined(_UNICODE) && !defined(__linux__) - std::wstring path = grf.getPath(); - const WCHAR *pchFilename=path.c_str(); - HANDLE fileHandle = CreateFile( - pchFilename, // file name - GENERIC_READ, // access mode - 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... - NULL, // Unused - OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it - FILE_FLAG_SEQUENTIAL_SCAN, // file attributes - NULL // Unsupported - ); -#else - const char *pchFilename=wstringtofilename(grf.getPath()); - HANDLE fileHandle = CreateFile( - pchFilename, // file name - GENERIC_READ, // access mode - 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... - NULL, // Unused - OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it - FILE_FLAG_SEQUENTIAL_SCAN, // file attributes - NULL // Unsupported - ); -#endif - - if( fileHandle != INVALID_HANDLE_VALUE ) + PBYTE pbData = NULL; + DWORD dwFileSize = 0; + if( ReadPortableBinaryFile(grf, pbData, dwFileSize) ) { - DWORD dwFileSize = grf.length(); - DWORD bytesRead; - PBYTE pbData = (PBYTE) new BYTE[dwFileSize]; - BOOL bSuccess = ReadFile(fileHandle,pbData,dwFileSize,&bytesRead,NULL); - if(bSuccess==FALSE) - { - app.FatalLoadError(); - } - CloseHandle(fileHandle); - // 4J-PB - is it possible that we can get here after a read fail and it's not an error? dlcFile->setGrfData(pbData, dwFileSize, texturePack->m_stringTable); @@ -398,6 +380,10 @@ int DLCTexturePack::packMounted(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicen app.m_gameRules.setLevelGenerationOptions( dlcFile->lgo ); } + else + { + app.FatalLoadError(); + } } } } @@ -406,45 +392,17 @@ int DLCTexturePack::packMounted(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicen File grf(getFilePath(texturePack->m_dlcInfoPack->GetPackID(), levelGen->getBaseSavePath() )); if (grf.exists()) { -#if defined(_UNICODE) && !defined(__linux__) - std::wstring path = grf.getPath(); - const WCHAR *pchFilename=path.c_str(); - HANDLE fileHandle = CreateFile( - pchFilename, // file name - GENERIC_READ, // access mode - 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... - NULL, // Unused - OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it - FILE_FLAG_SEQUENTIAL_SCAN, // file attributes - NULL // Unsupported - ); -#else - const char *pchFilename=wstringtofilename(grf.getPath()); - HANDLE fileHandle = CreateFile( - pchFilename, // file name - GENERIC_READ, // access mode - 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... - NULL, // Unused - OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it - FILE_FLAG_SEQUENTIAL_SCAN, // file attributes - NULL // Unsupported - ); -#endif - - if( fileHandle != INVALID_HANDLE_VALUE ) + PBYTE pbData = NULL; + DWORD dwFileSize = 0; + if( ReadPortableBinaryFile(grf, pbData, dwFileSize) ) { - DWORD bytesRead,dwFileSize = GetFileSize(fileHandle,NULL); - PBYTE pbData = (PBYTE) new BYTE[dwFileSize]; - BOOL bSuccess = ReadFile(fileHandle,pbData,dwFileSize,&bytesRead,NULL); - if(bSuccess==FALSE) - { - app.FatalLoadError(); - } - CloseHandle(fileHandle); - // 4J-PB - is it possible that we can get here after a read fail and it's not an error? levelGen->setBaseSaveData(pbData, dwFileSize); } + else + { + app.FatalLoadError(); + } } } } diff --git a/Minecraft.World/Util/PortableFileIO.h b/Minecraft.World/Util/PortableFileIO.h index e3e70a4ac..126a1448e 100644 --- a/Minecraft.World/Util/PortableFileIO.h +++ b/Minecraft.World/Util/PortableFileIO.h @@ -1,8 +1,7 @@ #pragma once #include -#include -#include +#include #include #include "StringHelpers.h" @@ -24,31 +23,72 @@ namespace PortableFileIO std::size_t fileSize; }; + inline std::FILE *OpenBinaryFileForRead(const std::wstring &path) + { +#if defined(_WIN32) + return _wfopen(path.c_str(), L"rb"); +#else + const std::string nativePath = wstringtofilename(path); + return std::fopen(nativePath.c_str(), "rb"); +#endif + } + + inline bool Seek(std::FILE *file, std::size_t offset, int origin) + { +#if defined(_WIN32) + return _fseeki64(file, static_cast<__int64>(offset), origin) == 0; +#else + return fseeko(file, static_cast(offset), origin) == 0; +#endif + } + + inline __int64 Tell(std::FILE *file) + { +#if defined(_WIN32) + return _ftelli64(file); +#else + return static_cast<__int64>(ftello(file)); +#endif + } + inline BinaryReadResult ReadBinaryFile(const std::wstring &path, void *buffer, std::size_t capacity) { - const std::string nativePath = wstringtofilename(path); - std::ifstream stream(nativePath.c_str(), std::ios::binary | std::ios::ate); - if (!stream.is_open()) + std::FILE *stream = OpenBinaryFileForRead(path); + if (stream == NULL) { return { BinaryReadStatus::not_found, 0, 0 }; } - const std::streamoff endPosition = stream.tellg(); + if (!Seek(stream, 0, SEEK_END)) + { + std::fclose(stream); + return { BinaryReadStatus::read_error, 0, 0 }; + } + + const __int64 endPosition = Tell(stream); if (endPosition < 0) { + std::fclose(stream); return { BinaryReadStatus::read_error, 0, 0 }; } const std::size_t fileSize = static_cast(endPosition); if (fileSize > capacity) { + std::fclose(stream); return { BinaryReadStatus::too_large, 0, fileSize }; } - stream.seekg(0, std::ios::beg); - stream.read(reinterpret_cast(buffer), static_cast(fileSize)); - const std::size_t bytesRead = static_cast(stream.gcount()); - if ((!stream && !stream.eof()) || bytesRead != fileSize) + if (!Seek(stream, 0, SEEK_SET)) + { + std::fclose(stream); + return { BinaryReadStatus::read_error, 0, fileSize }; + } + + const std::size_t bytesRead = std::fread(buffer, 1, fileSize, stream); + const bool failed = std::ferror(stream) != 0; + std::fclose(stream); + if (failed || bytesRead != fileSize) { return { BinaryReadStatus::read_error, bytesRead, fileSize }; } @@ -58,29 +98,42 @@ namespace PortableFileIO inline BinaryReadResult ReadBinaryFileSegment(const std::wstring &path, std::size_t offset, void *buffer, std::size_t bytesToRead) { - const std::string nativePath = wstringtofilename(path); - std::ifstream stream(nativePath.c_str(), std::ios::binary | std::ios::ate); - if (!stream.is_open()) + std::FILE *stream = OpenBinaryFileForRead(path); + if (stream == NULL) { return { BinaryReadStatus::not_found, 0, 0 }; } - const std::streamoff endPosition = stream.tellg(); + if (!Seek(stream, 0, SEEK_END)) + { + std::fclose(stream); + return { BinaryReadStatus::read_error, 0, 0 }; + } + + const __int64 endPosition = Tell(stream); if (endPosition < 0) { + std::fclose(stream); return { BinaryReadStatus::read_error, 0, 0 }; } const std::size_t fileSize = static_cast(endPosition); if ((offset > fileSize) || (bytesToRead > (fileSize - offset))) { + std::fclose(stream); return { BinaryReadStatus::too_large, 0, fileSize }; } - stream.seekg(static_cast(offset), std::ios::beg); - stream.read(reinterpret_cast(buffer), static_cast(bytesToRead)); - const std::size_t bytesRead = static_cast(stream.gcount()); - if ((!stream && !stream.eof()) || bytesRead != bytesToRead) + if (!Seek(stream, offset, SEEK_SET)) + { + std::fclose(stream); + return { BinaryReadStatus::read_error, 0, fileSize }; + } + + const std::size_t bytesRead = std::fread(buffer, 1, bytesToRead, stream); + const bool failed = std::ferror(stream) != 0; + std::fclose(stream); + if (failed || bytesRead != bytesToRead) { return { BinaryReadStatus::read_error, bytesRead, fileSize }; }