Use portable file reads for DLC texture data

This commit is contained in:
notmatthewbeshay 2026-03-09 23:39:09 +11:00
parent 2cc7a74e6f
commit 62a5c364f2
2 changed files with 123 additions and 112 deletions

View file

@ -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 <limits>
#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<DWORD>::max()))
{
data = NULL;
size = 0;
return false;
}
const std::size_t capacity = static_cast<std::size_t>(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<DWORD>::max())
{
delete [] buffer;
data = NULL;
size = 0;
return false;
}
data = buffer;
size = static_cast<DWORD>(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();
}
}
}
}

View file

@ -1,8 +1,7 @@
#pragma once
#include <cstddef>
#include <fstream>
#include <ios>
#include <cstdio>
#include <string>
#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<off_t>(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<std::size_t>(endPosition);
if (fileSize > capacity)
{
std::fclose(stream);
return { BinaryReadStatus::too_large, 0, fileSize };
}
stream.seekg(0, std::ios::beg);
stream.read(reinterpret_cast<char *>(buffer), static_cast<std::streamsize>(fileSize));
const std::size_t bytesRead = static_cast<std::size_t>(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<std::size_t>(endPosition);
if ((offset > fileSize) || (bytesToRead > (fileSize - offset)))
{
std::fclose(stream);
return { BinaryReadStatus::too_large, 0, fileSize };
}
stream.seekg(static_cast<std::streamoff>(offset), std::ios::beg);
stream.read(reinterpret_cast<char *>(buffer), static_cast<std::streamsize>(bytesToRead));
const std::size_t bytesRead = static_cast<std::size_t>(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 };
}