[Maps] Implement support for usermaps
This commit is contained in:
parent
0cf0b3ac16
commit
8bb3c1deca
@ -270,7 +270,8 @@ namespace Components
|
||||
void FileSystem::FsRestartSync(int a1, int a2)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
return Utils::Hook::Call<void(int, int)>(0x461A50)(a1, a2); // FS_Restart
|
||||
Utils::Hook::Call<void(int, int)>(0x461A50)(a1, a2); // FS_Restart
|
||||
Maps::GetUserMap()->reloadIwd();
|
||||
}
|
||||
|
||||
void FileSystem::DelayLoadImagesSync()
|
||||
@ -285,6 +286,12 @@ namespace Components
|
||||
return Game::Load_Texture(loadDef, image);
|
||||
}
|
||||
|
||||
void FileSystem::IwdFreeStub(Game::iwd_t* iwd)
|
||||
{
|
||||
Maps::GetUserMap()->handlePackfile(iwd);
|
||||
Utils::Hook::Call<void(void*)>(0x4291A0)(iwd);
|
||||
}
|
||||
|
||||
FileSystem::FileSystem()
|
||||
{
|
||||
FileSystem::MemAllocator.clear();
|
||||
@ -325,6 +332,9 @@ namespace Components
|
||||
// Synchronize db image loading
|
||||
Utils::Hook(0x415AB8, FileSystem::DelayLoadImagesSync, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x4D32BC, FileSystem::LoadTextureSync, HOOK_CALL).install()->quick();
|
||||
|
||||
// Handle IWD freeing
|
||||
Utils::Hook(0x642F60, FileSystem::IwdFreeStub, HOOK_CALL).install()->quick();
|
||||
}
|
||||
|
||||
FileSystem::~FileSystem()
|
||||
|
@ -115,5 +115,7 @@ namespace Components
|
||||
static void FsRestartSync(int a1, int a2);
|
||||
static void DelayLoadImagesSync();
|
||||
static int LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image);
|
||||
|
||||
static void IwdFreeStub(Game::iwd_t* iwd);
|
||||
};
|
||||
}
|
||||
|
@ -3,19 +3,102 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Maps::UserMapContainer Maps::UserMap;
|
||||
std::string Maps::CurrentMainZone;
|
||||
std::vector<std::pair<std::string, std::string>> Maps::DependencyList;
|
||||
std::vector<std::string> Maps::CurrentDependencies;
|
||||
|
||||
bool Maps::IsSPMap;
|
||||
bool Maps::SPMap;
|
||||
std::vector<Maps::DLC> Maps::DlcPacks;
|
||||
std::vector<Game::XAssetEntry> Maps::EntryPool;
|
||||
|
||||
Maps::UserMapContainer* Maps::GetUserMap()
|
||||
{
|
||||
return &Maps::UserMap;
|
||||
}
|
||||
|
||||
void Maps::UserMapContainer::loadIwd()
|
||||
{
|
||||
if (this->isValid() && !this->searchPath.iwd)
|
||||
{
|
||||
std::string iwdName = Utils::String::VA("%s.iwd", this->mapname.data());
|
||||
std::string path = Utils::String::VA("%s\\usermaps\\%s\\%s", Dvar::Var("fs_basepath").get<const char*>(), this->mapname.data(), iwdName.data());
|
||||
|
||||
this->searchPath.iwd = Game::FS_IsShippedIWD(path.data(), iwdName.data());
|
||||
|
||||
if (this->searchPath.iwd)
|
||||
{
|
||||
this->searchPath.bLocalized = false;
|
||||
this->searchPath.ignore = 0;
|
||||
this->searchPath.ignorePureCheck = 0;
|
||||
this->searchPath.language = 0;
|
||||
this->searchPath.dir = nullptr;
|
||||
this->searchPath.next = *Game::fs_searchpaths;
|
||||
*Game::fs_searchpaths = &this->searchPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Maps::UserMapContainer::reloadIwd()
|
||||
{
|
||||
if (this->isValid() && this->wasFreed)
|
||||
{
|
||||
this->wasFreed = false;
|
||||
this->searchPath.iwd = nullptr;
|
||||
this->loadIwd();
|
||||
}
|
||||
}
|
||||
|
||||
void Maps::UserMapContainer::handlePackfile(void* packfile)
|
||||
{
|
||||
if(this->isValid() && this->searchPath.iwd == packfile)
|
||||
{
|
||||
this->wasFreed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Maps::UserMapContainer::freeIwd()
|
||||
{
|
||||
if(this->isValid() && this->searchPath.iwd && !this->wasFreed)
|
||||
{
|
||||
this->wasFreed = true;
|
||||
|
||||
// Unchain our searchpath
|
||||
for(Game::searchpath_t** pathPtr = Game::fs_searchpaths; *pathPtr; pathPtr = &(*pathPtr)->next)
|
||||
{
|
||||
if(*pathPtr == &this->searchPath)
|
||||
{
|
||||
*pathPtr = (*pathPtr)->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Game::unzClose(this->searchPath.iwd->handle);
|
||||
|
||||
auto _free = Utils::Hook::Call<void(void*)>(0x6B5CF2);
|
||||
_free(this->searchPath.iwd->buildBuffer);
|
||||
_free(this->searchPath.iwd);
|
||||
|
||||
ZeroMemory(&this->searchPath, sizeof this->searchPath);
|
||||
}
|
||||
}
|
||||
|
||||
void Maps::UnloadMapZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync)
|
||||
{
|
||||
Game::DB_LoadXAssets(zoneInfo, zoneCount, sync);
|
||||
|
||||
if (Maps::UserMap.isValid())
|
||||
{
|
||||
Maps::UserMap.freeIwd();
|
||||
Maps::UserMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Maps::LoadMapZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync)
|
||||
{
|
||||
if (!zoneInfo) return;
|
||||
|
||||
Maps::IsSPMap = false;
|
||||
Maps::SPMap = false;
|
||||
Maps::CurrentMainZone = zoneInfo->name;
|
||||
|
||||
Maps::CurrentDependencies.clear();
|
||||
@ -160,7 +243,7 @@ namespace Components
|
||||
Logger::Print("Waiting for database...\n");
|
||||
while (!Game::Sys_IsDatabaseReady()) std::this_thread::sleep_for(100ms);
|
||||
|
||||
if (!Utils::String::StartsWith(Maps::CurrentMainZone, "mp_") || Maps::IsSPMap)
|
||||
if (!Utils::String::StartsWith(Maps::CurrentMainZone, "mp_") || Maps::SPMap)
|
||||
{
|
||||
return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP].gameWorldSp[0].data;
|
||||
}
|
||||
@ -217,7 +300,7 @@ namespace Components
|
||||
|
||||
void Maps::HandleAsSPMap()
|
||||
{
|
||||
Maps::IsSPMap = true;
|
||||
Maps::SPMap = true;
|
||||
}
|
||||
|
||||
void Maps::AddDependency(std::string expression, std::string zone)
|
||||
@ -290,6 +373,83 @@ namespace Components
|
||||
return { team_axis, team_allies };
|
||||
}
|
||||
|
||||
void Maps::PrepareUsermap(const char* mapname)
|
||||
{
|
||||
if(Maps::UserMap.isValid())
|
||||
{
|
||||
Maps::UserMap.freeIwd();
|
||||
Maps::UserMap.clear();
|
||||
}
|
||||
|
||||
if(Utils::IO::DirectoryExists(Utils::String::VA("usermaps/%s", mapname)) && Utils::IO::FileExists(Utils::String::VA("usermaps/%s/%s.ff", mapname, mapname)))
|
||||
{
|
||||
Maps::UserMap = Maps::UserMapContainer(mapname);
|
||||
Maps::UserMap.loadIwd();
|
||||
}
|
||||
else
|
||||
{
|
||||
Maps::UserMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Maps::GetUsermapHash(std::string map)
|
||||
{
|
||||
if (Utils::IO::DirectoryExists(Utils::String::VA("usermaps/%s", map.data())))
|
||||
{
|
||||
std::string zoneHash;
|
||||
std::string zonePath = Utils::String::VA("usermaps/%s/%s.ff", map.data(), map.data());
|
||||
if (Utils::IO::FileExists(zonePath))
|
||||
{
|
||||
zoneHash = Utils::Cryptography::SHA256::Compute(Utils::IO::ReadFile(zonePath));
|
||||
}
|
||||
|
||||
std::string iwdHash;
|
||||
std::string iwdPath = Utils::String::VA("usermaps/%s/%s.iwd", map.data(), map.data());
|
||||
if(Utils::IO::FileExists(iwdPath))
|
||||
{
|
||||
iwdHash = Utils::Cryptography::SHA256::Compute(Utils::IO::ReadFile(iwdPath));
|
||||
}
|
||||
|
||||
return Utils::Cryptography::JenkinsOneAtATime::Compute(zoneHash + iwdHash);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(naked) void Maps::SpawnServerStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
|
||||
push [esp + 24h]
|
||||
call Maps::PrepareUsermap
|
||||
pop eax
|
||||
|
||||
popad
|
||||
|
||||
push 4A7120h // SV_SpawnServer
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void Maps::LoadMapLoadscreenStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
|
||||
push[esp + 24h]
|
||||
call Maps::PrepareUsermap
|
||||
pop eax
|
||||
|
||||
popad
|
||||
|
||||
push 4D8030h // LoadMapLoadscreen
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG) && defined(ENABLE_DXSDK)
|
||||
// Credit to SE2Dev, as we shouldn't share the code, keep that in debug mode!
|
||||
void Maps::ExportMap(Game::GfxWorld* world)
|
||||
@ -638,8 +798,9 @@ namespace Components
|
||||
// Intercept BSP name resolving
|
||||
Utils::Hook(0x4C5979, Maps::GetBSPName, HOOK_CALL).install()->quick();
|
||||
|
||||
// Intercept map zone loading
|
||||
// Intercept map zone loading/unloading
|
||||
Utils::Hook(0x42C2AF, Maps::LoadMapZones, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x60B477, Maps::UnloadMapZones, HOOK_CALL).install()->quick();
|
||||
|
||||
// Ignore SP entities
|
||||
Utils::Hook(0x444810, Maps::IgnoreEntityStub, HOOK_JUMP).install()->quick();
|
||||
@ -650,6 +811,15 @@ namespace Components
|
||||
// Allow loading raw suns
|
||||
Utils::Hook(0x51B46A, Maps::LoadRawSun, HOOK_CALL).install()->quick();
|
||||
|
||||
// Intercept map loading for usermap initialization
|
||||
Utils::Hook(0x6245E3, Maps::SpawnServerStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x62493E, Maps::SpawnServerStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x42CF58, Maps::LoadMapLoadscreenStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x487CDD, Maps::LoadMapLoadscreenStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x4CA3E9, Maps::LoadMapLoadscreenStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x5A9D51, Maps::LoadMapLoadscreenStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x5B34DD, Maps::LoadMapLoadscreenStub, HOOK_CALL).install()->quick();
|
||||
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP, 1);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMAGE, 7168);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, 2700);
|
||||
|
@ -5,6 +5,41 @@ namespace Components
|
||||
class Maps : public Component
|
||||
{
|
||||
public:
|
||||
class UserMapContainer
|
||||
{
|
||||
public:
|
||||
UserMapContainer() : wasFreed(false) { }
|
||||
UserMapContainer(std::string _mapname) : wasFreed(false), mapname(_mapname)
|
||||
{
|
||||
ZeroMemory(&this->searchPath, sizeof this->searchPath);
|
||||
this->hash = Maps::GetUsermapHash(this->mapname);
|
||||
}
|
||||
|
||||
~UserMapContainer()
|
||||
{
|
||||
this->freeIwd();
|
||||
this->clear();
|
||||
}
|
||||
|
||||
unsigned int getHash() { return this->hash; }
|
||||
std::string getName() { return this->mapname; }
|
||||
bool isValid() { return !this->mapname.empty(); }
|
||||
void clear() { this->mapname.clear(); }
|
||||
|
||||
void loadIwd();
|
||||
void freeIwd();
|
||||
|
||||
void reloadIwd();
|
||||
|
||||
void handlePackfile(void* packfile);
|
||||
|
||||
private:
|
||||
bool wasFreed;
|
||||
unsigned int hash;
|
||||
std::string mapname;
|
||||
Game::searchpath_t searchPath;
|
||||
};
|
||||
|
||||
Maps();
|
||||
~Maps();
|
||||
|
||||
@ -22,6 +57,9 @@ namespace Components
|
||||
|
||||
static bool CheckMapInstalled(const char* mapname, bool error = false);
|
||||
|
||||
static UserMapContainer* GetUserMap();
|
||||
static unsigned int GetUsermapHash(std::string map);
|
||||
|
||||
private:
|
||||
class DLC
|
||||
{
|
||||
@ -31,7 +69,8 @@ namespace Components
|
||||
std::vector<std::string> maps;
|
||||
};
|
||||
|
||||
static bool IsSPMap;
|
||||
static bool SPMap;
|
||||
static UserMapContainer UserMap;
|
||||
static std::vector<DLC> DlcPacks;
|
||||
static std::vector<Game::XAssetEntry> EntryPool;
|
||||
|
||||
@ -41,6 +80,7 @@ namespace Components
|
||||
static void GetBSPName(char* buffer, size_t size, const char* format, const char* mapname);
|
||||
static void LoadAssetRestrict(Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool* restrict);
|
||||
static void LoadMapZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync);
|
||||
static void UnloadMapZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync);
|
||||
|
||||
static void OverrideMapEnts(Game::MapEnts* ents);
|
||||
|
||||
@ -58,6 +98,12 @@ namespace Components
|
||||
static void ExportMap(Game::GfxWorld* world);
|
||||
#endif
|
||||
|
||||
static void PrepareUsermap(const char* mapname);
|
||||
static void SpawnServerStub();
|
||||
static void LoadMapLoadscreenStub();
|
||||
|
||||
static void IwdFreeStub(Game::iwd_t* iwd);
|
||||
|
||||
void reallocateEntryPool();
|
||||
};
|
||||
}
|
||||
|
@ -657,11 +657,6 @@ namespace Components
|
||||
|
||||
Utils::IO::WriteFile("userraw/logs/matlog.txt", buffer);
|
||||
}
|
||||
|
||||
if (type == Game::XAssetType::ASSET_TYPE_CLIPMAP_PVS)
|
||||
{
|
||||
OutputDebugStringA("");
|
||||
}
|
||||
});
|
||||
|
||||
Dvar::OnInit([]
|
||||
@ -675,7 +670,7 @@ namespace Components
|
||||
|
||||
float cyan[4] = { 0.0f, 0.5f, 0.5f, 1.0f };
|
||||
|
||||
Game::clipMap_t* clipMap = *reinterpret_cast<Game::clipMap_t**>(0x7998E0);
|
||||
//Game::clipMap_t* clipMap = *reinterpret_cast<Game::clipMap_t**>(0x7998E0);
|
||||
Game::GfxWorld* gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
|
||||
if (!gameWorld) return;
|
||||
|
||||
|
@ -109,6 +109,7 @@ namespace Game
|
||||
FS_Remove_t FS_Remove = FS_Remove_t(0x4660F0);
|
||||
FS_Restart_t FS_Restart = FS_Restart_t(0x461A50);
|
||||
FS_BuildPathToFile_t FS_BuildPathToFile = FS_BuildPathToFile_t(0x4702C0);
|
||||
FS_IsShippedIWD_t FS_IsShippedIWD = FS_IsShippedIWD_t(0x642440);
|
||||
|
||||
G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString = G_SpawnEntitiesFromString_t(0x4D8840);
|
||||
|
||||
@ -274,6 +275,8 @@ namespace Game
|
||||
|
||||
Vec3UnpackUnitVec_t Vec3UnpackUnitVec = Vec3UnpackUnitVec_t(0x45CA90);
|
||||
|
||||
unzClose_t unzClose = unzClose_t(0x41BF20);
|
||||
|
||||
XAssetHeader* DB_XAssetPool = reinterpret_cast<XAssetHeader*>(0x7998A8);
|
||||
unsigned int* g_poolSize = reinterpret_cast<unsigned int*>(0x7995E8);
|
||||
|
||||
@ -301,7 +304,7 @@ namespace Game
|
||||
int* gameTypeCount = reinterpret_cast<int*>(0x62E50A0);
|
||||
gameTypeName_t* gameTypes = reinterpret_cast<gameTypeName_t*>(0x62E50A4);
|
||||
|
||||
searchpath_t* fs_searchpaths = reinterpret_cast<searchpath_t*>(0x63D96E0);
|
||||
searchpath_t** fs_searchpaths = reinterpret_cast<searchpath_t**>(0x63D96E0);
|
||||
|
||||
XBlock** g_streamBlocks = reinterpret_cast<XBlock**>(0x16E554C);
|
||||
int* g_streamPos = reinterpret_cast<int*>(0x16E5554);
|
||||
|
@ -282,6 +282,9 @@ namespace Game
|
||||
typedef int(__cdecl * FS_BuildPathToFile_t)(const char*, const char*, const char*, char**);
|
||||
extern FS_BuildPathToFile_t FS_BuildPathToFile;
|
||||
|
||||
typedef iwd_t*(__cdecl * FS_IsShippedIWD_t)(const char* fullpath, const char* iwd);
|
||||
extern FS_IsShippedIWD_t FS_IsShippedIWD;
|
||||
|
||||
typedef void(__cdecl* G_SpawnEntitiesFromString_t)();
|
||||
extern G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString;
|
||||
|
||||
@ -658,6 +661,9 @@ namespace Game
|
||||
typedef void (__cdecl * Vec3UnpackUnitVec_t)(PackedUnitVec, vec3_t *);
|
||||
extern Vec3UnpackUnitVec_t Vec3UnpackUnitVec;
|
||||
|
||||
typedef void(__cdecl * unzClose_t)(void* handle);
|
||||
extern unzClose_t unzClose;
|
||||
|
||||
extern XAssetHeader* DB_XAssetPool;
|
||||
extern unsigned int* g_poolSize;
|
||||
|
||||
@ -685,7 +691,7 @@ namespace Game
|
||||
extern int* gameTypeCount;
|
||||
extern gameTypeName_t* gameTypes;
|
||||
|
||||
extern searchpath_t* fs_searchpaths;
|
||||
extern searchpath_t** fs_searchpaths;
|
||||
|
||||
extern XBlock** g_streamBlocks;
|
||||
extern int* g_streamPos;
|
||||
|
Loading…
Reference in New Issue
Block a user