2017-01-16 11:42:50 -05:00
# include "STDInclude.hpp"
namespace Components
{
2017-04-06 16:22:30 -04:00
Maps : : UserMapContainer Maps : : UserMap ;
2017-01-16 11:42:50 -05:00
std : : string Maps : : CurrentMainZone ;
std : : vector < std : : pair < std : : string , std : : string > > Maps : : DependencyList ;
std : : vector < std : : string > Maps : : CurrentDependencies ;
2017-04-06 16:22:30 -04:00
bool Maps : : SPMap ;
2017-01-16 11:42:50 -05:00
std : : vector < Maps : : DLC > Maps : : DlcPacks ;
2017-04-08 09:32:51 -04:00
const char * Maps : : UserMapFiles [ 4 ] =
{
" .ff " ,
" _load.ff " ,
" .iwd " ,
" .arena " ,
} ;
2017-04-06 16:22:30 -04:00
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 ( ) ) ;
2017-04-09 12:27:58 -04:00
if ( Utils : : IO : : FileExists ( path ) )
2017-04-06 16:22:30 -04:00
{
2017-04-09 12:27:58 -04:00
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 ;
}
2017-04-06 16:22:30 -04:00
}
}
}
void Maps : : UserMapContainer : : reloadIwd ( )
{
if ( this - > isValid ( ) & & this - > wasFreed )
{
this - > wasFreed = false ;
this - > searchPath . iwd = nullptr ;
this - > loadIwd ( ) ;
}
}
void Maps : : UserMapContainer : : handlePackfile ( void * packfile )
{
2017-04-07 15:37:01 -04:00
if ( this - > isValid ( ) & & this - > searchPath . iwd = = packfile )
2017-04-06 16:22:30 -04:00
{
this - > wasFreed = true ;
}
}
void Maps : : UserMapContainer : : freeIwd ( )
{
2017-04-07 15:37:01 -04:00
if ( this - > isValid ( ) & & this - > searchPath . iwd & & ! this - > wasFreed )
2017-04-06 16:22:30 -04:00
{
this - > wasFreed = true ;
// Unchain our searchpath
2017-04-07 15:37:01 -04:00
for ( Game : : searchpath_t * * pathPtr = Game : : fs_searchpaths ; * pathPtr ; pathPtr = & ( * pathPtr ) - > next )
2017-04-06 16:22:30 -04:00
{
2017-04-07 15:37:01 -04:00
if ( * pathPtr = = & this - > searchPath )
2017-04-06 16:22:30 -04:00
{
* 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 ) ;
}
}
2017-04-08 09:32:51 -04:00
const char * Maps : : LoadArenaFileStub ( const char * name , char * buffer , int size )
{
std : : string data = Game : : LoadModdableRawfile ( 0 , name ) ;
if ( Maps : : UserMap . isValid ( ) )
{
std : : string mapname = Maps : : UserMap . getName ( ) ;
std : : string arena = Utils : : String : : VA ( " usermaps/%s/%s.arena " , mapname . data ( ) , mapname . data ( ) ) ;
if ( Utils : : IO : : FileExists ( arena ) )
{
data . append ( Utils : : IO : : ReadFile ( arena ) ) ;
}
}
strncpy_s ( buffer , size , data . data ( ) , data . size ( ) ) ;
return buffer ;
}
2017-04-07 15:37:01 -04:00
void Maps : : UnloadMapZones ( Game : : XZoneInfo * zoneInfo , unsigned int zoneCount , int sync )
2017-04-06 16:22:30 -04:00
{
Game : : DB_LoadXAssets ( zoneInfo , zoneCount , sync ) ;
if ( Maps : : UserMap . isValid ( ) )
{
Maps : : UserMap . freeIwd ( ) ;
Maps : : UserMap . clear ( ) ;
}
}
2017-04-07 15:37:01 -04:00
void Maps : : LoadMapZones ( Game : : XZoneInfo * zoneInfo , unsigned int zoneCount , int sync )
2017-01-16 11:42:50 -05:00
{
if ( ! zoneInfo ) return ;
2017-04-06 16:22:30 -04:00
Maps : : SPMap = false ;
2017-01-16 11:42:50 -05:00
Maps : : CurrentMainZone = zoneInfo - > name ;
2019-10-02 02:08:11 -04:00
2017-01-16 11:42:50 -05:00
Maps : : CurrentDependencies . clear ( ) ;
for ( auto i = Maps : : DependencyList . begin ( ) ; i ! = Maps : : DependencyList . end ( ) ; + + i )
{
if ( std : : regex_match ( zoneInfo - > name , std : : regex ( i - > first ) ) )
{
if ( std : : find ( Maps : : CurrentDependencies . begin ( ) , Maps : : CurrentDependencies . end ( ) , i - > second ) = = Maps : : CurrentDependencies . end ( ) )
{
Maps : : CurrentDependencies . push_back ( i - > second ) ;
}
}
}
Utils : : Memory : : Allocator allocator ;
auto teams = Maps : : GetTeamsForMap ( Maps : : CurrentMainZone ) ;
auto dependencies = Maps : : GetDependenciesForMap ( Maps : : CurrentMainZone ) ;
Utils : : Merge ( & Maps : : CurrentDependencies , dependencies . data ( ) , dependencies . size ( ) ) ;
std : : vector < Game : : XZoneInfo > data ;
Utils : : Merge ( & data , zoneInfo , zoneCount ) ;
2019-10-02 02:08:11 -04:00
2017-01-16 11:42:50 -05:00
Game : : XZoneInfo team ;
team . allocFlags = zoneInfo - > allocFlags ;
team . freeFlags = zoneInfo - > freeFlags ;
team . name = allocator . duplicateString ( Utils : : String : : VA ( " iw4x_team_%s " , teams . first . data ( ) ) ) ;
data . push_back ( team ) ;
team . name = allocator . duplicateString ( Utils : : String : : VA ( " iw4x_team_%s " , teams . second . data ( ) ) ) ;
data . push_back ( team ) ;
for ( unsigned int i = 0 ; i < Maps : : CurrentDependencies . size ( ) ; + + i )
{
Game : : XZoneInfo info ;
info . name = ( & Maps : : CurrentDependencies [ i ] ) - > data ( ) ;
info . allocFlags = zoneInfo - > allocFlags ;
info . freeFlags = zoneInfo - > freeFlags ;
data . push_back ( info ) ;
}
// Load patch files
std : : string patchZone = Utils : : String : : VA ( " patch_%s " , zoneInfo - > name ) ;
if ( FastFiles : : Exists ( patchZone ) )
{
2017-04-07 15:37:01 -04:00
data . push_back ( { patchZone . data ( ) , zoneInfo - > allocFlags , zoneInfo - > freeFlags } ) ;
2017-01-16 11:42:50 -05:00
}
return FastFiles : : LoadLocalizeZones ( data . data ( ) , data . size ( ) , sync ) ;
}
void Maps : : OverrideMapEnts ( Game : : MapEnts * ents )
{
auto callback = [ ] ( Game : : XAssetHeader header , void * ents )
{
2017-04-07 15:37:01 -04:00
Game : : MapEnts * mapEnts = reinterpret_cast < Game : : MapEnts * > ( ents ) ;
Game : : clipMap_t * clipMap = header . clipMap ;
if ( clipMap & & mapEnts & & ! _stricmp ( mapEnts - > name , clipMap - > name ) )
{
clipMap - > mapEnts = mapEnts ;
//*Game::marMapEntsPtr = mapEnts;
//Game::G_SpawnEntitiesFromString();
}
} ;
2017-01-16 11:42:50 -05:00
// Internal doesn't lock the thread, as locking is impossible, due to executing this in the thread that holds the current lock
2018-05-09 06:04:20 -04:00
Game : : DB_EnumXAssets_Internal ( Game : : XAssetType : : ASSET_TYPE_CLIPMAP_MP , callback , ents , true ) ;
Game : : DB_EnumXAssets_Internal ( Game : : XAssetType : : ASSET_TYPE_CLIPMAP_SP , callback , ents , true ) ;
2017-01-16 11:42:50 -05:00
}
2018-12-17 08:29:18 -05:00
void Maps : : LoadAssetRestrict ( Game : : XAssetType type , Game : : XAssetHeader asset , const std : : string & name , bool * restrict )
2017-01-16 11:42:50 -05:00
{
if ( std : : find ( Maps : : CurrentDependencies . begin ( ) , Maps : : CurrentDependencies . end ( ) , FastFiles : : Current ( ) ) ! = Maps : : CurrentDependencies . end ( )
& & ( FastFiles : : Current ( ) ! = " mp_shipment_long " | | Maps : : CurrentMainZone ! = " mp_shipment " ) ) // Shipment is a special case
{
2022-01-30 09:03:13 -05:00
switch ( type )
2017-01-16 11:42:50 -05:00
{
2022-01-30 09:03:13 -05:00
case Game : : XAssetType : : ASSET_TYPE_CLIPMAP_MP :
case Game : : XAssetType : : ASSET_TYPE_CLIPMAP_SP :
case Game : : XAssetType : : ASSET_TYPE_GAMEWORLD_SP :
case Game : : XAssetType : : ASSET_TYPE_GAMEWORLD_MP :
case Game : : XAssetType : : ASSET_TYPE_GFXWORLD :
case Game : : XAssetType : : ASSET_TYPE_MAP_ENTS :
case Game : : XAssetType : : ASSET_TYPE_COMWORLD :
case Game : : XAssetType : : ASSET_TYPE_FXWORLD :
2017-01-16 11:42:50 -05:00
* restrict = true ;
return ;
}
}
if ( type = = Game : : XAssetType : : ASSET_TYPE_ADDON_MAP_ENTS )
{
* restrict = true ;
return ;
}
if ( type = = Game : : XAssetType : : ASSET_TYPE_WEAPON )
{
if ( ( ! strstr ( name . data ( ) , " _mp " ) & & name ! = " none " & & name ! = " destructible_car " ) | | Zones : : Version ( ) > = VERSION_ALPHA2 )
{
* restrict = true ;
return ;
}
}
if ( type = = Game : : XAssetType : : ASSET_TYPE_STRINGTABLE )
{
if ( FastFiles : : Current ( ) = = " mp_cross_fire " )
{
* restrict = true ;
return ;
}
}
if ( type = = Game : : XAssetType : : ASSET_TYPE_MAP_ENTS )
{
if ( Flags : : HasFlag ( " dump " ) )
{
2022-01-27 09:32:47 -05:00
Utils : : IO : : WriteFile ( Utils : : String : : VA ( " raw/%s.ents " , name . data ( ) ) , asset . mapEnts - > entityString , true ) ;
2017-01-16 11:42:50 -05:00
}
2022-02-08 11:41:49 -05:00
static std : : string mapEntities ;
2017-01-16 11:42:50 -05:00
FileSystem : : File ents ( name + " .ents " ) ;
if ( ents . exists ( ) )
{
2022-02-08 11:41:49 -05:00
mapEntities = ents . getBuffer ( ) ;
2022-01-31 07:53:57 -05:00
asset . mapEnts - > entityString = mapEntities . data ( ) ;
2017-01-16 11:42:50 -05:00
asset . mapEnts - > numEntityChars = mapEntities . size ( ) + 1 ;
}
}
2022-01-31 07:53:57 -05:00
2017-01-16 11:42:50 -05:00
// This is broken
if ( ( type = = Game : : XAssetType : : ASSET_TYPE_MENU | | type = = Game : : XAssetType : : ASSET_TYPE_MENULIST ) & & Zones : : Version ( ) > = 359 )
{
* restrict = true ;
return ;
}
}
Game : : G_GlassData * Maps : : GetWorldData ( )
{
2017-05-28 16:21:26 -04:00
Logger : : Print ( " Waiting for database... \n " ) ;
while ( ! Game : : Sys_IsDatabaseReady ( ) ) std : : this_thread : : sleep_for ( 10 ms ) ;
2019-10-02 02:08:11 -04:00
if ( ! Game : : DB_XAssetPool [ Game : : XAssetType : : ASSET_TYPE_GAMEWORLD_MP ] . gameWorldMp | | ! Game : : DB_XAssetPool [ Game : : XAssetType : : ASSET_TYPE_GAMEWORLD_MP ] . gameWorldMp - > name | |
! Game : : DB_XAssetPool [ Game : : XAssetType : : ASSET_TYPE_GAMEWORLD_MP ] . gameWorldMp - > g_glassData | | Maps : : SPMap )
2017-01-16 11:42:50 -05:00
{
2019-10-02 02:08:11 -04:00
return Game : : DB_XAssetPool [ Game : : XAssetType : : ASSET_TYPE_GAMEWORLD_SP ] . gameWorldSp - > g_glassData ;
2017-01-16 11:42:50 -05:00
}
2019-10-02 02:08:11 -04:00
return Game : : DB_XAssetPool [ Game : : XAssetType : : ASSET_TYPE_GAMEWORLD_MP ] . gameWorldMp - > g_glassData ;
2017-01-16 11:42:50 -05:00
}
__declspec ( naked ) void Maps : : GetWorldDataStub ( )
{
__asm
{
push eax
pushad
call Maps : : GetWorldData
mov [ esp + 20 h ] , eax
popad
pop eax
retn
}
}
void Maps : : LoadRawSun ( )
{
Game : : R_FlushSun ( ) ;
Game : : GfxWorld * world = * reinterpret_cast < Game : : GfxWorld * * > ( 0x66DEE94 ) ;
if ( FileSystem : : File ( Utils : : String : : VA ( " sun/%s.sun " , Maps : : CurrentMainZone . data ( ) ) ) . exists ( ) )
{
Game : : R_LoadSunThroughDvars ( Maps : : CurrentMainZone . data ( ) , & world - > sun ) ;
}
}
void Maps : : GetBSPName ( char * buffer , size_t size , const char * format , const char * mapname )
{
if ( ! Utils : : String : : StartsWith ( mapname , " mp_ " ) & & ! Utils : : String : : StartsWith ( mapname , " zm_ " ) )
{
format = " maps/%s.d3dbsp " ;
}
// Redirect shipment to shipment long
if ( mapname = = " mp_shipment " s )
{
mapname = " mp_shipment_long " ;
}
2022-01-30 09:03:13 -05:00
_snprintf_s ( buffer , size , _TRUNCATE , format , mapname ) ;
2017-01-16 11:42:50 -05:00
}
void Maps : : HandleAsSPMap ( )
{
2017-04-06 16:22:30 -04:00
Maps : : SPMap = true ;
2017-01-16 11:42:50 -05:00
}
2018-12-17 08:29:18 -05:00
void Maps : : AddDependency ( const std : : string & expression , const std : : string & zone )
2017-01-16 11:42:50 -05:00
{
2022-01-30 13:52:55 -05:00
// Test expression before adding it
2017-01-16 11:42:50 -05:00
try
{
std : : regex _ ( expression ) ;
}
2022-01-30 13:52:55 -05:00
catch ( const std : : regex_error ex )
2017-01-16 11:42:50 -05:00
{
2017-01-20 16:41:03 -05:00
MessageBoxA ( nullptr , Utils : : String : : VA ( " Invalid regular expression: %s " , expression . data ( ) ) , " Warning " , MB_ICONEXCLAMATION ) ;
2017-01-16 11:42:50 -05:00
return ;
}
2017-04-07 15:37:01 -04:00
Maps : : DependencyList . push_back ( { expression , zone } ) ;
2017-01-16 11:42:50 -05:00
}
int Maps : : IgnoreEntityStub ( const char * entity )
{
return ( Utils : : String : : StartsWith ( entity , " dyn_ " ) | | Utils : : String : : StartsWith ( entity , " node_ " ) | | Utils : : String : : StartsWith ( entity , " actor_ " ) ) ;
}
2018-12-17 08:29:18 -05:00
std : : vector < std : : string > Maps : : GetDependenciesForMap ( const std : : string & map )
2017-01-16 11:42:50 -05:00
{
for ( int i = 0 ; i < * Game : : arenaCount ; + + i )
{
Game : : newMapArena_t * arena = & ArenaLength : : NewArenas [ i ] ;
if ( arena - > mapName = = map )
{
2017-01-27 08:43:52 -05:00
for ( int j = 0 ; j < ARRAYSIZE ( arena - > keys ) ; + + j )
2017-01-16 11:42:50 -05:00
{
if ( arena - > keys [ j ] = = " dependency " s )
{
2022-02-26 17:50:53 -05:00
return Utils : : String : : Split ( arena - > values [ j ] , ' ' ) ;
2017-01-16 11:42:50 -05:00
}
}
}
}
return { } ;
}
2018-12-17 08:29:18 -05:00
std : : pair < std : : string , std : : string > Maps : : GetTeamsForMap ( const std : : string & map )
2017-01-16 11:42:50 -05:00
{
std : : string team_axis = " opforce_composite " ;
std : : string team_allies = " us_army " ;
for ( int i = 0 ; i < * Game : : arenaCount ; + + i )
{
Game : : newMapArena_t * arena = & ArenaLength : : NewArenas [ i ] ;
if ( arena - > mapName = = map )
{
2017-01-27 08:43:52 -05:00
for ( int j = 0 ; j < ARRAYSIZE ( arena - > keys ) ; + + j )
2017-01-16 11:42:50 -05:00
{
if ( arena - > keys [ j ] = = " allieschar " s )
{
team_allies = arena - > values [ j ] ;
}
else if ( arena - > keys [ j ] = = " axischar " s )
{
team_axis = arena - > values [ j ] ;
}
}
break ;
}
}
2017-04-07 15:37:01 -04:00
return { team_axis , team_allies } ;
2017-01-16 11:42:50 -05:00
}
2017-04-06 16:22:30 -04:00
void Maps : : PrepareUsermap ( const char * mapname )
{
2017-04-07 15:37:01 -04:00
if ( Maps : : UserMap . isValid ( ) )
2017-04-06 16:22:30 -04:00
{
Maps : : UserMap . freeIwd ( ) ;
Maps : : UserMap . clear ( ) ;
}
2017-07-12 13:36:18 -04:00
if ( Maps : : IsUserMap ( mapname ) )
2017-04-06 16:22:30 -04:00
{
Maps : : UserMap = Maps : : UserMapContainer ( mapname ) ;
Maps : : UserMap . loadIwd ( ) ;
}
else
{
Maps : : UserMap . clear ( ) ;
}
}
2018-12-17 08:29:18 -05:00
unsigned int Maps : : GetUsermapHash ( const std : : string & map )
2017-04-06 16:22:30 -04:00
{
if ( Utils : : IO : : DirectoryExists ( Utils : : String : : VA ( " usermaps/%s " , map . data ( ) ) ) )
{
2017-04-08 09:32:51 -04:00
std : : string hash ;
2017-04-06 16:22:30 -04:00
2017-04-08 09:32:51 -04:00
for ( int i = 0 ; i < ARRAYSIZE ( Maps : : UserMapFiles ) ; + + i )
2017-04-06 16:22:30 -04:00
{
2017-04-08 09:32:51 -04:00
std : : string filePath = Utils : : String : : VA ( " usermaps/%s/%s%s " , map . data ( ) , map . data ( ) , Maps : : UserMapFiles [ i ] ) ;
if ( Utils : : IO : : FileExists ( filePath ) )
{
hash . append ( Utils : : Cryptography : : SHA256 : : Compute ( Utils : : IO : : ReadFile ( filePath ) ) ) ;
}
2017-04-06 16:22:30 -04:00
}
2017-04-08 09:32:51 -04:00
return Utils : : Cryptography : : JenkinsOneAtATime : : Compute ( hash ) ;
2017-04-06 16:22:30 -04:00
}
return 0 ;
}
2017-05-30 15:49:13 -04:00
void Maps : : LoadNewMapCommand ( char * buffer , size_t size , const char * /*format*/ , const char * mapname , const char * gametype )
{
unsigned int hash = Maps : : GetUsermapHash ( mapname ) ;
2022-01-30 09:03:13 -05:00
_snprintf_s ( buffer , size , _TRUNCATE , " loadingnewmap \n %s \n %s \n %d " , mapname , gametype , hash ) ;
2017-05-30 15:49:13 -04:00
}
int Maps : : TriggerReconnectForMap ( Game : : msg_t * msg , const char * mapname )
2017-04-07 15:37:01 -04:00
{
Theatre : : StopRecording ( ) ;
2017-05-30 15:49:13 -04:00
char hashBuf [ 100 ] = { 0 } ;
unsigned int hash = atoi ( Game : : MSG_ReadStringLine ( msg , hashBuf , sizeof hashBuf ) ) ;
if ( ! Maps : : CheckMapInstalled ( mapname , false , true ) | | hash & & hash ! = Maps : : GetUsermapHash ( mapname ) )
2017-04-07 15:37:01 -04:00
{
// Reconnecting forces the client to download the new map
Command : : Execute ( " disconnect " , false ) ;
Command : : Execute ( " awaitDatabase " , false ) ; // Wait for the database to load
Command : : Execute ( " wait 100 " , false ) ;
Command : : Execute ( " openmenu popup_reconnectingtoparty " , false ) ;
2017-05-25 14:18:20 -04:00
Command : : Execute ( " delayReconnect " , false ) ;
2017-04-07 15:37:01 -04:00
return true ;
}
return false ;
}
__declspec ( naked ) void Maps : : RotateCheckStub ( )
{
__asm
{
push eax
pushad
push [ esp + 28 h ]
2017-05-30 15:49:13 -04:00
push ebp
2017-04-07 15:37:01 -04:00
call Maps : : TriggerReconnectForMap
2017-05-30 15:49:13 -04:00
add esp , 8 h
2017-04-07 15:37:01 -04:00
2017-06-14 06:06:04 -04:00
mov [ esp + 20 h ] , eax
2017-04-07 15:37:01 -04:00
popad
pop eax
test eax , eax
jnz skipRotation
push 487 C50h // Rotate map
skipRotation :
retn
}
}
2017-04-06 16:22:30 -04:00
__declspec ( naked ) void Maps : : SpawnServerStub ( )
{
__asm
{
pushad
push [ esp + 24 h ]
call Maps : : PrepareUsermap
pop eax
popad
push 4 A7120h // SV_SpawnServer
retn
}
}
__declspec ( naked ) void Maps : : LoadMapLoadscreenStub ( )
{
__asm
{
pushad
2017-04-07 15:37:01 -04:00
push [ esp + 24 h ]
2017-04-06 16:22:30 -04:00
call Maps : : PrepareUsermap
pop eax
popad
push 4 D8030h // LoadMapLoadscreen
retn
}
}
2017-01-16 11:42:50 -05:00
void Maps : : AddDlc ( Maps : : DLC dlc )
{
for ( auto & pack : Maps : : DlcPacks )
{
if ( pack . index = = dlc . index )
{
pack . maps = dlc . maps ;
Maps : : UpdateDlcStatus ( ) ;
return ;
}
}
Dvar : : Register < bool > ( Utils : : String : : VA ( " isDlcInstalled_%d " , dlc . index ) , false , Game : : DVAR_FLAG_USERCREATED | Game : : DVAR_FLAG_WRITEPROTECTED , " " ) ;
Maps : : DlcPacks . push_back ( dlc ) ;
Maps : : UpdateDlcStatus ( ) ;
}
void Maps : : UpdateDlcStatus ( )
{
bool hasAllDlcs = true ;
2017-02-10 08:06:42 -05:00
std : : vector < bool > hasDlc ;
2017-01-16 11:42:50 -05:00
for ( auto & pack : Maps : : DlcPacks )
{
bool hasAllMaps = true ;
for ( auto map : pack . maps )
{
if ( ! FastFiles : : Exists ( map ) )
{
hasAllMaps = false ;
hasAllDlcs = false ;
break ;
}
}
2017-02-10 08:06:42 -05:00
hasDlc . push_back ( hasAllMaps ) ;
2021-09-10 06:40:53 -04:00
Dvar : : Var ( Utils : : String : : VA ( " isDlcInstalled_%d " , pack . index ) ) . set ( hasAllMaps ? true : false ) ;
2017-01-16 11:42:50 -05:00
}
2017-01-30 21:57:31 -05:00
// Must have all of dlc 3 to 5 or it causes issues
static bool sentMessage = false ;
2017-02-10 08:06:42 -05:00
if ( hasDlc . size ( ) > = 5 & & ( hasDlc [ 2 ] | | hasDlc [ 3 ] | | hasDlc [ 4 ] ) & & ( ! hasDlc [ 2 ] | | ! hasDlc [ 3 ] | | ! hasDlc [ 4 ] ) & & ! sentMessage )
2017-01-30 21:57:31 -05:00
{
StartupMessages : : AddMessage ( " Warning: \n You only have some of DLCs 3-5 which are all required to be installed to work. There may be issues with those maps. " ) ;
sentMessage = true ;
}
2021-09-10 06:40:53 -04:00
Dvar : : Var ( " isDlcInstalled_All " ) . set ( hasAllDlcs ? true : false ) ;
2017-01-16 11:42:50 -05:00
}
2017-05-01 08:57:17 -04:00
bool Maps : : IsCustomMap ( )
{
Game : : GfxWorld * & gameWorld = * reinterpret_cast < Game : : GfxWorld * * > ( 0x66DEE94 ) ;
if ( gameWorld ) return gameWorld - > checksum = = 0xDEADBEEF ;
return false ;
}
2018-12-17 08:29:18 -05:00
bool Maps : : IsUserMap ( const std : : string & mapname )
2017-07-12 13:36:18 -04:00
{
return Utils : : IO : : DirectoryExists ( Utils : : String : : VA ( " usermaps/%s " , mapname . data ( ) ) ) & & Utils : : IO : : FileExists ( Utils : : String : : VA ( " usermaps/%s/%s.ff " , mapname . data ( ) , mapname . data ( ) ) ) ;
}
2017-05-01 07:08:34 -04:00
Game : : XAssetEntry * Maps : : GetAssetEntryPool ( )
{
2017-06-21 13:56:20 -04:00
return * reinterpret_cast < Game : : XAssetEntry * * > ( 0x48E6F4 ) ;
2017-01-16 11:42:50 -05:00
}
2017-04-07 15:37:01 -04:00
// dlcIsTrue serves as a check if the map is a custom map and if it's missing
bool Maps : : CheckMapInstalled ( const char * mapname , bool error , bool dlcIsTrue )
2017-01-29 12:27:11 -05:00
{
if ( FastFiles : : Exists ( mapname ) ) return true ;
for ( auto & pack : Maps : : DlcPacks )
{
for ( auto map : pack . maps )
{
if ( map = = std : : string ( mapname ) )
{
2017-04-07 15:37:01 -04:00
if ( error )
{
Components : : Logger : : SoftError ( " Missing DLC pack %s (%d) containing map %s (%s). \n Please download it to play this map. " ,
pack . name . data ( ) , pack . index , Game : : UI_LocalizeMapName ( mapname ) , mapname ) ;
}
return dlcIsTrue ;
2017-01-29 12:27:11 -05:00
}
}
}
2017-04-07 15:37:01 -04:00
if ( error ) Components : : Logger : : SoftError ( " Missing map file %s. \n You may have a damaged installation or are attempting to load a non-existant map. " , mapname ) ;
2017-01-29 12:27:11 -05:00
return false ;
}
2017-04-24 15:13:54 -04:00
void Maps : : HideModel ( )
{
Game : : GfxWorld * & gameWorld = * reinterpret_cast < Game : : GfxWorld * * > ( 0x66DEE94 ) ;
if ( ! gameWorld ) return ;
std : : string model = Dvar : : Var ( " r_hideModel " ) . get < std : : string > ( ) ;
if ( model . empty ( ) ) return ;
for ( unsigned int i = 0 ; i < gameWorld - > dpvs . smodelCount ; + + i )
{
if ( gameWorld - > dpvs . smodelDrawInsts [ i ] . model - > name = = model )
{
gameWorld - > dpvs . smodelVisData [ 0 ] [ i ] = 0 ;
gameWorld - > dpvs . smodelVisData [ 1 ] [ i ] = 0 ;
gameWorld - > dpvs . smodelVisData [ 2 ] [ i ] = 0 ;
}
}
}
__declspec ( naked ) void Maps : : HideModelStub ( )
{
__asm
{
pushad
call Maps : : HideModel
popad
push 541E40 h
retn
}
}
2017-05-01 08:57:17 -04:00
Game : : dvar_t * Maps : : GetDistortionDvar ( )
{
Game : : dvar_t * & r_distortion = * reinterpret_cast < Game : : dvar_t * * > ( 0x69F0DCC ) ;
if ( Maps : : IsCustomMap ( ) )
{
static Game : : dvar_t noDistortion ;
ZeroMemory ( & noDistortion , sizeof noDistortion ) ;
return & noDistortion ;
}
return r_distortion ;
}
__declspec ( naked ) void Maps : : SetDistortionStub ( )
{
__asm
{
push eax
pushad
call Maps : : GetDistortionDvar
mov [ esp + 20 h ] , eax
popad
pop eax
retn
}
}
2017-05-29 14:43:15 -04:00
Game : : dvar_t * Maps : : GetSpecularDvar ( )
{
Game : : dvar_t * & r_specular = * reinterpret_cast < Game : : dvar_t * * > ( 0x69F0D94 ) ;
2020-09-01 09:07:52 -04:00
static Game : : dvar_t * r_specularCustomMaps = Game : : Dvar_RegisterBool ( " r_specularCustomMaps " , false , Game : : DVAR_FLAG_SAVED , " Allows shaders to use phong specular lighting on custom maps " ) ;
2017-05-29 14:43:15 -04:00
if ( Maps : : IsCustomMap ( ) )
{
2020-07-22 23:36:14 -04:00
if ( ! r_specularCustomMaps - > current . enabled )
{
static Game : : dvar_t noSpecular ;
ZeroMemory ( & noSpecular , sizeof noSpecular ) ;
return & noSpecular ;
}
2017-05-29 14:43:15 -04:00
}
return r_specular ;
}
__declspec ( naked ) void Maps : : SetSpecularStub1 ( )
{
__asm
{
push eax
pushad
call Maps : : GetSpecularDvar
2017-06-14 06:06:04 -04:00
mov [ esp + 20 h ] , eax
2017-05-29 14:43:15 -04:00
popad
pop eax
retn
}
}
__declspec ( naked ) void Maps : : SetSpecularStub2 ( )
{
__asm
{
push eax
pushad
call Maps : : GetSpecularDvar
2017-06-14 06:06:04 -04:00
mov [ esp + 20 h ] , eax
2017-05-29 14:43:15 -04:00
popad
pop edx
retn
}
}
2019-10-02 02:08:11 -04:00
void Maps : : G_SpawnTurretHook ( Game : : gentity_s * ent , int unk , int unk2 )
{
if ( Maps : : CurrentMainZone = = " mp_backlot_sh " s | | Maps : : CurrentMainZone = = " mp_con_spring " s | |
Maps : : CurrentMainZone = = " mp_mogadishu_sh " s | | Maps : : CurrentMainZone = = " mp_nightshift_sh " s )
{
return ;
}
Utils : : Hook : : Call < void ( Game : : gentity_s * , int , int ) > ( 0x408910 ) ( ent , unk , unk2 ) ;
}
2021-04-02 10:13:51 -04:00
bool Maps : : SV_SetTriggerModelHook ( Game : : gentity_s * ent ) {
2021-04-05 12:13:00 -04:00
// Use me for debugging
//std::string classname = Game::SL_ConvertToString(ent->script_classname);
//std::string targetname = Game::SL_ConvertToString(ent->targetname);
2021-04-02 10:13:51 -04:00
return Utils : : Hook : : Call < bool ( Game : : gentity_s * ) > ( 0x5050C0 ) ( ent ) ;
}
int16 Maps : : CM_TriggerModelBounds ( int modelPointer , Game : : Bounds * bounds ) {
2021-04-06 05:33:14 -04:00
# ifdef DEBUG
Game : : MapEnts * ents = * reinterpret_cast < Game : : MapEnts * * > ( 0x1AA651C ) ; // Use me for debugging
2021-11-07 03:27:01 -05:00
( void ) ents ;
2021-04-06 05:33:14 -04:00
# endif
2021-04-02 10:13:51 -04:00
return Utils : : Hook : : Call < int16 ( int , Game : : Bounds * ) > ( 0x4416C0 ) ( modelPointer , bounds ) ;
}
2019-10-02 02:08:11 -04:00
2017-01-16 11:42:50 -05:00
Maps : : Maps ( )
{
2017-04-07 16:07:44 -04:00
Dvar : : OnInit ( [ ] ( )
{
Dvar : : Register < bool > ( " isDlcInstalled_All " , false , Game : : DVAR_FLAG_USERCREATED | Game : : DVAR_FLAG_WRITEPROTECTED , " " ) ;
2017-04-24 15:13:54 -04:00
Dvar : : Register < bool > ( " r_listSModels " , false , Game : : DVAR_FLAG_NONE , " Display a list of visible SModels " ) ;
2017-04-07 16:07:44 -04:00
Maps : : AddDlc ( { 1 , " Stimulus Pack " , { " mp_complex " , " mp_compact " , " mp_storm " , " mp_overgrown " , " mp_crash " } } ) ;
2017-04-24 15:13:54 -04:00
Maps : : AddDlc ( { 2 , " Resurgence Pack " , { " mp_abandon " , " mp_vacant " , " mp_trailerpark " , " mp_strike " , " mp_fuel2 " } } ) ;
2017-04-07 16:07:44 -04:00
Maps : : AddDlc ( { 3 , " Nuketown " , { " mp_nuked " } } ) ;
2019-10-02 02:08:11 -04:00
Maps : : AddDlc ( { 4 , " Classics Pack #1 " , { " mp_cross_fire " , " mp_cargoship " , " mp_bloc " } } ) ;
Maps : : AddDlc ( { 5 , " Classics Pack #2 " , { " mp_killhouse " , " mp_bog_sh " } } ) ;
2017-04-07 16:07:44 -04:00
Maps : : AddDlc ( { 6 , " Freighter " , { " mp_cargoship_sh " } } ) ;
Maps : : AddDlc ( { 7 , " Resurrection Pack " , { " mp_shipment_long " , " mp_rust_long " , " mp_firingrange " } } ) ;
Maps : : AddDlc ( { 8 , " Recycled Pack " , { " mp_bloc_sh " , " mp_crash_tropical " , " mp_estate_tropical " , " mp_fav_tropical " , " mp_storm_spring " } } ) ;
2021-09-12 14:36:42 -04:00
Maps : : AddDlc ( { 9 , " Classics Pack #3 " , { " mp_farm " , " mp_backlot " , " mp_pipeline " , " mp_countdown " , " mp_crash_snow " , " mp_carentan " } } ) ;
2017-04-07 16:07:44 -04:00
Maps : : UpdateDlcStatus ( ) ;
UIScript : : Add ( " downloadDLC " , [ ] ( UIScript : : Token token )
{
int dlc = token . get < int > ( ) ;
2017-01-16 11:42:50 -05:00
2017-04-07 16:07:44 -04:00
for ( auto pack : Maps : : DlcPacks )
{
if ( pack . index = = dlc )
{
2022-01-26 07:27:52 -05:00
ShellExecuteW ( 0 , 0 , L " https://xlabs.dev/support_iw4x_client.html " , 0 , 0 , SW_SHOW ) ;
2017-04-07 16:07:44 -04:00
return ;
}
}
Game : : ShowMessageBox ( Utils : : String : : VA ( " DLC %d does not exist! " , dlc ) , " ERROR " ) ;
2017-01-16 11:42:50 -05:00
} ) ;
2017-04-07 16:07:44 -04:00
} ) ;
2017-01-16 11:42:50 -05:00
2019-10-02 02:08:11 -04:00
// disable turrets on CoD:OL 448+ maps for now
Utils : : Hook ( 0x5EE577 , Maps : : G_SpawnTurretHook , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x44A4D5 , Maps : : G_SpawnTurretHook , HOOK_CALL ) . install ( ) - > quick ( ) ;
2021-04-02 10:13:51 -04:00
2021-04-05 12:13:00 -04:00
# ifdef DEBUG
2021-04-02 10:13:51 -04:00
// Check trigger models
Utils : : Hook ( 0x5FC0F1 , Maps : : SV_SetTriggerModelHook , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x5FC2671 , Maps : : SV_SetTriggerModelHook , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x5050D4 , Maps : : CM_TriggerModelBounds , HOOK_CALL ) . install ( ) - > quick ( ) ;
2021-04-05 12:13:00 -04:00
# endif
2021-04-02 10:13:51 -04:00
//
2019-10-02 02:08:11 -04:00
2017-04-24 15:13:54 -04:00
//#define SORT_SMODELS
# if !defined(DEBUG) || !defined(SORT_SMODELS)
2017-04-16 08:29:35 -04:00
// Don't sort static models
Utils : : Hook : : Nop ( 0x53D815 , 2 ) ;
# endif
2017-04-20 15:27:38 -04:00
// Don't draw smodels
//Utils::Hook::Set<BYTE>(0x541E40, 0xC3);
2017-01-16 11:42:50 -05:00
// Restrict asset loading
AssetHandler : : OnLoad ( Maps : : LoadAssetRestrict ) ;
// hunk size (was 300 MiB)
Utils : : Hook : : Set < DWORD > ( 0x64A029 , 0x1C200000 ) ; // 450 MiB
Utils : : Hook : : Set < DWORD > ( 0x64A057 , 0x1C200000 ) ;
# if DEBUG
// Hunk debugging
Utils : : Hook : : Set < BYTE > ( 0x4FF57B , 0xCC ) ;
Utils : : Hook : : Nop ( 0x4FF57C , 4 ) ;
2017-05-27 11:26:12 -04:00
# else
// Temporarily disable distortion warnings
Utils : : Hook : : Nop ( 0x50DBFF , 5 ) ;
Utils : : Hook : : Nop ( 0x50DC4F , 5 ) ;
Utils : : Hook : : Nop ( 0x50DCA3 , 5 ) ;
Utils : : Hook : : Nop ( 0x50DCFE , 5 ) ;
2017-01-16 11:42:50 -05:00
# endif
// Intercept BSP name resolving
Utils : : Hook ( 0x4C5979 , Maps : : GetBSPName , HOOK_CALL ) . install ( ) - > quick ( ) ;
2017-04-06 16:22:30 -04:00
// Intercept map zone loading/unloading
2017-01-16 11:42:50 -05:00
Utils : : Hook ( 0x42C2AF , Maps : : LoadMapZones , HOOK_CALL ) . install ( ) - > quick ( ) ;
2017-04-06 16:22:30 -04:00
Utils : : Hook ( 0x60B477 , Maps : : UnloadMapZones , HOOK_CALL ) . install ( ) - > quick ( ) ;
2017-01-16 11:42:50 -05:00
// Ignore SP entities
Utils : : Hook ( 0x444810 , Maps : : IgnoreEntityStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
// WorldData pointer replacement
Utils : : Hook ( 0x4D90B6 , Maps : : GetWorldDataStub , HOOK_CALL ) . install ( ) - > quick ( ) ;
// Allow loading raw suns
Utils : : Hook ( 0x51B46A , Maps : : LoadRawSun , HOOK_CALL ) . install ( ) - > quick ( ) ;
2017-05-27 14:50:22 -04:00
// Disable distortion on custom maps
2017-05-30 13:22:49 -04:00
//Utils::Hook(0x50AA47, Maps::SetDistortionStub, HOOK_CALL).install()->quick();
2017-05-01 08:57:17 -04:00
2017-05-29 14:43:15 -04:00
// Disable speculars on custom maps
Utils : : Hook ( 0x525EA6 , Maps : : SetSpecularStub1 , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x51FBC7 , Maps : : SetSpecularStub2 , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x522A2E , Maps : : SetSpecularStub2 , HOOK_CALL ) . install ( ) - > quick ( ) ;
Utils : : Hook : : Nop ( 0x51FBCC , 1 ) ;
Utils : : Hook : : Nop ( 0x522A33 , 1 ) ;
2017-04-06 16:22:30 -04:00
// 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 ( ) ;
2017-05-25 14:18:20 -04:00
Command : : Add ( " delayReconnect " , [ ] ( Command : : Params * )
{
2017-05-31 10:09:41 -04:00
Scheduler : : OnDelay ( [ ] ( )
2017-05-25 14:18:20 -04:00
{
Command : : Execute ( " closemenu popup_reconnectingtoparty " , false ) ;
Command : : Execute ( " reconnect " , false ) ;
2018-11-25 17:19:21 -05:00
} , 10 s , true ) ;
2017-05-25 14:18:20 -04:00
} ) ;
2017-05-30 15:49:13 -04:00
if ( Dedicated : : IsEnabled ( ) )
{
Utils : : Hook ( 0x4A7251 , Maps : : LoadNewMapCommand , HOOK_CALL ) . install ( ) - > quick ( ) ;
}
2017-04-07 15:37:01 -04:00
// Download the map before a maprotation if necessary
// Conflicts with Theater's SV map rotation check, but this one is safer!
Utils : : Hook ( 0x5AA91C , Maps : : RotateCheckStub , HOOK_CALL ) . install ( ) - > quick ( ) ;
2017-04-08 09:32:51 -04:00
// Load usermap arena file
Utils : : Hook ( 0x630A88 , Maps : : LoadArenaFileStub , HOOK_CALL ) . install ( ) - > quick ( ) ;
2017-01-16 11:42:50 -05:00
// Dependencies
//Maps::AddDependency("oilrig", "mp_subbase");
//Maps::AddDependency("gulag", "mp_subbase");
//Maps::AddDependency("invasion", "mp_rust");
//Maps::AddDependency("co_hunted", "mp_storm");
//Maps::AddDependency("mp_shipment", "mp_shipment_long");
2017-04-24 15:13:54 -04:00
// Allow hiding specific smodels
Utils : : Hook ( 0x50E67C , Maps : : HideModelStub , HOOK_CALL ) . install ( ) - > quick ( ) ;
2017-05-31 10:09:41 -04:00
Scheduler : : OnFrame ( [ ] ( )
2017-04-24 15:13:54 -04:00
{
Game : : GfxWorld * & gameWorld = * reinterpret_cast < Game : : GfxWorld * * > ( 0x66DEE94 ) ;
if ( ! Game : : CL_IsCgameInitialized ( ) | | ! gameWorld | | ! Dvar : : Var ( " r_listSModels " ) . get < bool > ( ) ) return ;
std : : map < std : : string , int > models ;
for ( unsigned int i = 0 ; i < gameWorld - > dpvs . smodelCount ; + + i )
{
if ( gameWorld - > dpvs . smodelVisData [ 0 ] [ i ] )
{
std : : string name = gameWorld - > dpvs . smodelDrawInsts [ i ] . model - > name ;
if ( models . find ( name ) = = models . end ( ) ) models [ name ] = 1 ;
else models [ name ] + + ;
}
}
2018-05-09 08:33:52 -04:00
Game : : Font_s * font = Game : : R_RegisterFont ( " fonts/smallFont " , 0 ) ;
2017-04-24 15:13:54 -04:00
int height = Game : : R_TextHeight ( font ) ;
float scale = 0.75 ;
float color [ 4 ] = { 0 , 1.0f , 0 , 1.0f } ;
unsigned int i = 0 ;
for ( auto & model : models )
{
Game : : R_AddCmdDrawText ( Utils : : String : : VA ( " %d %s " , model . second , model . first . data ( ) ) , 0x7FFFFFFF , font , 15.0f , ( height * scale + 1 ) * ( i + + + 1 ) + 15.0f , scale , scale , 0.0f , color , Game : : ITEM_TEXTSTYLE_NORMAL ) ;
}
2017-06-01 04:25:13 -04:00
} , true ) ;
2017-01-16 11:42:50 -05:00
}
Maps : : ~ Maps ( )
{
Maps : : DlcPacks . clear ( ) ;
Maps : : DependencyList . clear ( ) ;
Maps : : CurrentMainZone . clear ( ) ;
Maps : : CurrentDependencies . clear ( ) ;
}
}