[Maps] Implement support for usermaps

This commit is contained in:
momo5502 2017-04-06 22:22:30 +02:00
parent 0cf0b3ac16
commit 8bb3c1deca
7 changed files with 247 additions and 15 deletions

View File

@ -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()

View File

@ -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);
};
}

View File

@ -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);

View File

@ -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();
};
}

View File

@ -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;

View File

@ -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);

View File

@ -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;