dlc fixes

This commit is contained in:
Fireblade 2026-05-11 21:16:28 -04:00
parent 0c944cb713
commit d32e62027c
6 changed files with 339 additions and 73 deletions

View file

@ -4,6 +4,7 @@
#include "../Minecraft.World/compression.h"
#include "ArchiveFile.h"
#include "lce_filesystem/FolderFile.h"
void ArchiveFile::_readHeader(DataInputStream *dis)
{
@ -28,10 +29,19 @@ void ArchiveFile::_readHeader(DataInputStream *dis)
}
}
ArchiveFile::ArchiveFile(File file)
ArchiveFile::ArchiveFile(File file, bool allowFolder)
{
m_cachedData = nullptr;
m_folderFile = nullptr;
m_useFolder = false;
m_sourcefile = file;
if(allowFolder && file.exists() && file.isDirectory())
{
m_folderFile = new FolderFile(file.getPath());
m_useFolder = true;
return;
}
app.DebugPrintf("Loading archive file...\n");
#ifndef _CONTENT_PACKAGE
char buf[256];
@ -72,10 +82,15 @@ ArchiveFile::ArchiveFile(File file)
ArchiveFile::~ArchiveFile()
{
delete m_cachedData;
delete m_folderFile;
}
vector<wstring> *ArchiveFile::getFileList()
{
if(m_useFolder)
{
return m_folderFile->getFileList();
}
vector<wstring> *out = new vector<wstring>();
for ( const auto& it : m_index )
@ -86,16 +101,28 @@ vector<wstring> *ArchiveFile::getFileList()
bool ArchiveFile::hasFile(const wstring &filename)
{
if(m_useFolder)
{
return m_folderFile->hasFile(filename);
}
return m_index.find(filename) != m_index.end();
}
int ArchiveFile::getFileSize(const wstring &filename)
{
if(m_useFolder)
{
return m_folderFile->getFileSize(filename);
}
return hasFile(filename) ? m_index.at(filename)->filesize : -1;
}
byteArray ArchiveFile::getFile(const wstring &filename)
{
if(m_useFolder)
{
return m_folderFile->getFile(filename);
}
byteArray out;
auto it = m_index.find(filename);

View file

@ -8,11 +8,15 @@
using namespace std;
class FolderFile;
class ArchiveFile
{
protected:
File m_sourcefile;
BYTE *m_cachedData;
FolderFile *m_folderFile;
bool m_useFolder;
typedef struct _MetaData
{
@ -28,7 +32,7 @@ protected:
public:
void _readHeader(DataInputStream *dis);
ArchiveFile(File file);
ArchiveFile(File file, bool allowFolder = false);
~ArchiveFile();
vector<wstring> *getFileList();

View file

@ -4,9 +4,56 @@
#include "DLCPack.h"
#include "DLCFile.h"
#include "../../../Minecraft.World/StringHelpers.h"
#include "../../../Minecraft.World/File.h"
#include "../../Minecraft.h"
#include "../../TexturePackRepository.h"
#include "Common/UI/UI.h"
#include "lce_filesystem/FolderFile.h"
static bool hasPckFolderFallback(const wstring &path, wstring &folderPath)
{
wstring lowerPath = toLower(path);
const wstring pckSuffix = L".pck";
if(lowerPath.size() <= pckSuffix.size())
{
return false;
}
if(lowerPath.compare(lowerPath.size() - pckSuffix.size(), pckSuffix.size(), pckSuffix) != 0)
{
return false;
}
if(!(lowerPath.find(L"x16Data.pck") != wstring::npos
|| lowerPath.find(L"x32Data.pck") != wstring::npos))
{
return false;
}
folderPath = path.substr(0, path.size() - pckSuffix.size());
return true;
}
static DLCManager::EDLCType getFolderFileType(const wstring &path)
{
wstring lowerPath = toLower(path);
if(lowerPath == L"colours.col" || lowerPath.rfind(L"/colours.col") != wstring::npos)
{
return DLCManager::e_DLCType_ColourTable;
}
if(lowerPath.rfind(L"/languages.loc") != wstring::npos || lowerPath.rfind(L".loc") != wstring::npos)
{
return DLCManager::e_DLCType_LocalisationData;
}
if(lowerPath.rfind(L".xzp") != wstring::npos)
{
return DLCManager::e_DLCType_UIData;
}
if(lowerPath.rfind(L".grf") != wstring::npos)
{
return DLCManager::e_DLCType_GameRulesHeader;
}
return DLCManager::e_DLCType_Texture;
}
const WCHAR *DLCManager::wchTypeNamesA[]=
{
@ -328,13 +375,47 @@ bool DLCManager::readDLCDataFile(DWORD &dwFilesProcessed, const string &path, DL
}
else if (fromArchive) return false;
wstring finalPathW = wPath;
#ifdef _WINDOWS64
string finalPath = StorageManager.GetMountedPath(path.c_str());
if(finalPath.size() == 0) finalPath = path;
HANDLE file = CreateFile(finalPath.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
finalPathW = convStringToWstring(finalPath);
#elif defined(_DURANGO)
wstring finalPath = StorageManager.GetMountedPath(wPath.c_str());
if(finalPath.size() == 0) finalPath = wPath;
finalPathW = finalPath;
#endif
if(!fromArchive)
{
wstring folderPath;
if(hasPckFolderFallback(finalPathW, folderPath))
{
wstring resolvedFolderPath = folderPath;
#ifdef _WINDOWS64
const char *folderPathA = wstringtofilename(folderPath);
string mountedFolderPath = StorageManager.GetMountedPath(folderPathA);
if(mountedFolderPath.size() > 0)
{
resolvedFolderPath = convStringToWstring(mountedFolderPath);
}
#elif defined(_DURANGO)
wstring mountedFolderPath = StorageManager.GetMountedPath(folderPath.c_str());
if(mountedFolderPath.size() > 0)
{
resolvedFolderPath = mountedFolderPath;
}
#endif
if(readDLCDataFolder(dwFilesProcessed, resolvedFolderPath, pack))
{
return true;
}
}
}
#ifdef _WINDOWS64
HANDLE file = CreateFile(finalPath.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
#elif defined(_DURANGO)
HANDLE file = CreateFile(finalPath.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
#else
HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
@ -371,6 +452,81 @@ bool DLCManager::readDLCDataFile(DWORD &dwFilesProcessed, const string &path, DL
return processDLCDataFile(dwFilesProcessed, pbData, bytesRead, pack);
}
bool DLCManager::readDLCDataFolder(DWORD &dwFilesProcessed, const wstring &path, DLCPack *pack)
{
File folder(path);
if(!folder.exists() || !folder.isDirectory())
{
return false;
}
FolderFile folderFile(path);
vector<wstring> *fileList = folderFile.getFileList();
if(fileList == nullptr || fileList->empty())
{
delete fileList;
return false;
}
struct FolderEntry
{
wstring rawPath;
wstring normalizedPath;
int size;
};
vector<FolderEntry> entries;
entries.reserve(fileList->size());
unsigned int totalBytes = 0;
for(const auto &rawPath : *fileList)
{
wstring normalized = replaceAll(rawPath, L"\\", L"/");
if(normalized.empty() || normalized == L"0")
{
continue;
}
int size = folderFile.getFileSize(rawPath);
if(size <= 0)
{
continue;
}
entries.push_back({ rawPath, normalized, size });
totalBytes += static_cast<unsigned int>(size);
}
delete fileList;
if(entries.empty() || totalBytes == 0)
{
return false;
}
PBYTE dataBuffer = new BYTE[totalBytes];
pack->SetDataPointer(dataBuffer);
unsigned int offset = 0;
for(const auto &entry : entries)
{
byteArray data = folderFile.getFile(entry.rawPath);
if(data.data == nullptr || data.length == 0)
{
continue;
}
DLCManager::EDLCType type = getFolderFileType(entry.normalizedPath);
DLCFile *dlcFile = pack->addFile(type, entry.normalizedPath);
if(dlcFile != nullptr)
{
memcpy(dataBuffer + offset, data.data, data.length);
dlcFile->addData(dataBuffer + offset, data.length);
offset += data.length;
++dwFilesProcessed;
}
delete [] data.data;
}
return dwFilesProcessed > 0;
}
bool DLCManager::processDLCDataFile(DWORD &dwFilesProcessed, PBYTE pbData, DWORD dwLength, DLCPack *pack)
{
unordered_map<int, EDLCParameterType> parameterMapping;

View file

@ -93,6 +93,7 @@ public:
bool readDLCDataFile(DWORD &dwFilesProcessed, const wstring &path, DLCPack *pack, bool fromArchive = false);
bool readDLCDataFile(DWORD &dwFilesProcessed, const string &path, DLCPack *pack, bool fromArchive = false);
bool readDLCDataFolder(DWORD &dwFilesProcessed, const wstring &path, DLCPack *pack);
DWORD retrievePackIDFromDLCDataFile(const string &path, DLCPack *pack);
static unsigned short SwapInt16(unsigned short value) {

View file

@ -988,6 +988,13 @@ int CGameNetworkManager::RunNetworkGameThreadProc( void* lpParameter )
int CGameNetworkManager::ServerThreadProc( void* lpParameter )
{
int64_t seed = 0;
Compression::UseDefaultThreadStorage();
if (Compression::getCompression() == nullptr)
{
Compression::CreateNewThreadStorage();
}
if (lpParameter != nullptr)
{
NetworkGameInitData *param = static_cast<NetworkGameInitData *>(lpParameter);
@ -1027,6 +1034,7 @@ int CGameNetworkManager::ServerThreadProc( void* lpParameter )
AABB::ReleaseThreadStorage();
Vec3::ReleaseThreadStorage();
IntCache::ReleaseThreadStorage();
Compression::ReleaseThreadStorage();
Level::destroyLightingCache();
if(lpParameter != nullptr) delete lpParameter;

View file

@ -144,9 +144,27 @@ InputStream *DLCTexturePack::getResourceImplementation(const wstring &name) //th
bool DLCTexturePack::hasFile(const wstring &name)
{
bool hasFile = false;
if(m_dlcDataPack != nullptr) hasFile = m_dlcDataPack->doesPackContainFile(DLCManager::e_DLCType_Texture, name);
return hasFile;
if(m_dlcDataPack == nullptr)
{
return false;
}
wstring normalized = replaceAll(name, L"\\", L"/");
if(!normalized.empty() && normalized[0] == L'/')
{
normalized = normalized.substr(1);
}
if(normalized.rfind(L"res/", 0) != 0)
{
normalized = L"res/" + normalized;
}
if(m_dlcDataPack->doesPackContainFile(DLCManager::e_DLCType_Texture, normalized))
{
return true;
}
return false;
}
bool DLCTexturePack::isTerrainUpdateCompatible()
@ -163,10 +181,23 @@ wstring DLCTexturePack::getAnimationString(const wstring &textureName, const wst
{
wstring result = L"";
wstring fullpath = L"res/" + path + textureName + L".png";
if(hasFile(fullpath))
wstring fullpath = path;
if(!fullpath.empty() && fullpath[0] == L'/')
{
result = m_dlcDataPack->getFile(DLCManager::e_DLCType_Texture, fullpath)->getParameterAsString(DLCManager::e_DLCParamType_Anim);
fullpath = fullpath.substr(1);
}
if(fullpath.rfind(L"res/", 0) != 0)
{
fullpath = L"res/" + fullpath;
}
fullpath += textureName + L".png";
if(m_dlcDataPack != nullptr && m_dlcDataPack->doesPackContainFile(DLCManager::e_DLCType_Texture, fullpath))
{
DLCFile *dlcFile = m_dlcDataPack->getFile(DLCManager::e_DLCType_Texture, fullpath);
if(dlcFile != nullptr)
{
result = dlcFile->getParameterAsString(DLCManager::e_DLCParamType_Anim);
}
}
return result;
@ -174,8 +205,33 @@ wstring DLCTexturePack::getAnimationString(const wstring &textureName, const wst
BufferedImage *DLCTexturePack::getImageResource(const wstring& File, bool filenameHasExtension /*= false*/, bool bTitleUpdateTexture /*=false*/, const wstring &drive /*=L""*/)
{
if(m_dlcDataPack) return new BufferedImage(m_dlcDataPack, L"/" + File, filenameHasExtension);
else return fallback->getImageResource(File, filenameHasExtension, bTitleUpdateTexture, drive);
if(m_dlcDataPack != nullptr)
{
wstring dlcPath = File;
if(!dlcPath.empty() && dlcPath[0] == L'/')
{
dlcPath = dlcPath.substr(1);
}
if(dlcPath.rfind(L"res/", 0) != 0)
{
dlcPath = L"res/" + dlcPath;
}
bool hasTexture = m_dlcDataPack->doesPackContainFile(DLCManager::e_DLCType_Texture, dlcPath);
if(!hasTexture && !filenameHasExtension)
{
hasTexture = m_dlcDataPack->doesPackContainFile(DLCManager::e_DLCType_Texture, dlcPath + L".png");
}
if(!hasTexture && filenameHasExtension && dlcPath.size() > 4)
{
wstring noExt = dlcPath.substr(0, dlcPath.size() - 4);
hasTexture = m_dlcDataPack->doesPackContainFile(DLCManager::e_DLCType_Texture, noExt + L".png");
}
if(hasTexture)
{
return new BufferedImage(m_dlcDataPack, L"/" + File, filenameHasExtension);
}
}
return fallback->getImageResource(File, filenameHasExtension, bTitleUpdateTexture, drive);
}
DLCPack * DLCTexturePack::getDLCPack()
@ -354,8 +410,16 @@ int DLCTexturePack::packMounted(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicen
}
}
#else
File archivePath(getFilePath(texturePack->m_dlcInfoPack->GetPackID(), wstring(L"media.arc") ) );
if(archivePath.exists()) texturePack->m_archiveFile = new ArchiveFile(archivePath);
File mediaFolder(getFilePath(texturePack->m_dlcInfoPack->GetPackID(), wstring(L"media")));
if(mediaFolder.exists() && mediaFolder.isDirectory())
{
texturePack->m_archiveFile = new ArchiveFile(mediaFolder, true);
}
else
{
File archivePath(getFilePath(texturePack->m_dlcInfoPack->GetPackID(), wstring(L"media.arc") ) );
if(archivePath.exists()) texturePack->m_archiveFile = new ArchiveFile(archivePath);
}
#endif
/**
@ -364,16 +428,73 @@ int DLCTexturePack::packMounted(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicen
*/
DLCPack *pack = texturePack->m_dlcInfoPack->GetParentPack();
LevelGenerationOptions *levelGen = app.getLevelGenerationOptions();
if (levelGen != nullptr && !levelGen->hasLoadedData())
bool shouldLoadGameRules = levelGen != nullptr && levelGen->isFromDLC();
if (shouldLoadGameRules)
{
int gameRulesCount = pack->getDLCItemsCount(DLCManager::e_DLCType_GameRulesHeader);
for(int i = 0; i < gameRulesCount; ++i)
bool needsReload = !levelGen->hasLoadedData()
|| levelGen->getRequiredTexturePackId() != texturePack->getDLCParentPackId();
if(needsReload)
{
DLCGameRulesHeader *dlcFile = static_cast<DLCGameRulesHeader *>(pack->getFile(DLCManager::e_DLCType_GameRulesHeader, i));
if (!dlcFile->getGrfPath().empty())
int gameRulesCount = pack->getDLCItemsCount(DLCManager::e_DLCType_GameRulesHeader);
for(int i = 0; i < gameRulesCount; ++i)
{
File grf( getFilePath(texturePack->m_dlcInfoPack->GetPackID(), dlcFile->getGrfPath() ) );
DLCGameRulesHeader *dlcFile = static_cast<DLCGameRulesHeader *>(pack->getFile(DLCManager::e_DLCType_GameRulesHeader, i));
if (!dlcFile->getGrfPath().empty())
{
File grf( getFilePath(texturePack->m_dlcInfoPack->GetPackID(), dlcFile->getGrfPath() ) );
if (grf.exists())
{
#ifdef _UNICODE
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...
nullptr, // 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
nullptr // 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...
nullptr, // 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
nullptr // Unsupported
);
#endif
if( fileHandle != INVALID_HANDLE_VALUE )
{
DWORD dwFileSize = grf.length();
DWORD bytesRead;
PBYTE pbData = (PBYTE) new BYTE[dwFileSize];
BOOL bSuccess = ReadFile(fileHandle,pbData,dwFileSize,&bytesRead,nullptr);
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);
delete [] pbData;
app.m_gameRules.setLevelGenerationOptions( dlcFile->lgo );
}
}
}
}
if(levelGen->requiresBaseSave() && !levelGen->getBaseSavePath().empty() )
{
File grf(getFilePath(texturePack->m_dlcInfoPack->GetPackID(), levelGen->getBaseSavePath() ));
if (grf.exists())
{
#ifdef _UNICODE
@ -403,8 +524,7 @@ int DLCTexturePack::packMounted(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicen
if( fileHandle != INVALID_HANDLE_VALUE )
{
DWORD dwFileSize = grf.length();
DWORD bytesRead;
DWORD bytesRead,dwFileSize = GetFileSize(fileHandle,nullptr);
PBYTE pbData = (PBYTE) new BYTE[dwFileSize];
BOOL bSuccess = ReadFile(fileHandle,pbData,dwFileSize,&bytesRead,nullptr);
if(bSuccess==FALSE)
@ -414,61 +534,11 @@ int DLCTexturePack::packMounted(LPVOID pParam,int iPad,DWORD dwErr,DWORD dwLicen
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);
delete [] pbData;
app.m_gameRules.setLevelGenerationOptions( dlcFile->lgo );
levelGen->setBaseSaveData(pbData, dwFileSize);
}
}
}
}
if(levelGen->requiresBaseSave() && !levelGen->getBaseSavePath().empty() )
{
File grf(getFilePath(texturePack->m_dlcInfoPack->GetPackID(), levelGen->getBaseSavePath() ));
if (grf.exists())
{
#ifdef _UNICODE
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...
nullptr, // 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
nullptr // 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...
nullptr, // 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
nullptr // Unsupported
);
#endif
if( fileHandle != INVALID_HANDLE_VALUE )
{
DWORD bytesRead,dwFileSize = GetFileSize(fileHandle,nullptr);
PBYTE pbData = (PBYTE) new BYTE[dwFileSize];
BOOL bSuccess = ReadFile(fileHandle,pbData,dwFileSize,&bytesRead,nullptr);
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);
}
}
}
}