2016-07-11 11:14:58 -04:00
# include "STDInclude.hpp"
namespace Components
{
2016-12-21 20:48:00 -05:00
Utils : : Memory : : Allocator ZoneBuilder : : MemAllocator ;
2016-07-11 11:14:58 -04:00
std : : string ZoneBuilder : : TraceZone ;
std : : vector < std : : pair < Game : : XAssetType , std : : string > > ZoneBuilder : : TraceAssets ;
2017-01-13 09:44:46 -05:00
std : : vector < std : : pair < Game : : XAssetType , std : : string > > ZoneBuilder : : CommonAssets ;
2016-11-20 14:41:38 -05:00
ZoneBuilder : : Zone : : Zone ( std : : string name ) : dataMap ( " zone_source/ " + name + " .csv " ) , zoneName ( name ) , indexStart ( 0 ) , externalSize ( 0 ) , branding { 0 } ,
2016-07-11 11:14:58 -04:00
// Reserve 100MB by default.
// That's totally fine, as the dedi doesn't load images and therefore doesn't need much memory.
// That way we can be sure it won't need to reallocate memory.
// Side note: if you need a fastfile larger than 100MB, you're doing it wrong-
// Well, decompressed maps can get way larger than 100MB, so let's increase that.
2016-11-20 08:09:07 -05:00
buffer ( 0xC800000 )
2016-07-11 11:14:58 -04:00
{ }
ZoneBuilder : : Zone : : ~ Zone ( )
{
2016-12-24 16:11:36 -05:00
# ifdef DEBUG
for ( auto & subAsset : this - > loadedSubAssets )
{
bool found = false ;
std : : string name = Game : : DB_GetXAssetName ( & subAsset ) ;
for ( auto & alias : this - > aliasList )
{
if ( subAsset . type = = alias . first . type & & name = = Game : : DB_GetXAssetName ( & alias . first ) )
{
found = true ;
break ;
}
}
if ( ! found )
{
Logger : : Error ( " Asset %s of type %s was loaded, but not written! " , name . data ( ) , Game : : DB_GetXAssetTypeName ( subAsset . type ) ) ;
}
}
for ( auto & alias : this - > aliasList )
{
bool found = false ;
std : : string name = Game : : DB_GetXAssetName ( & alias . first ) ;
for ( auto & subAsset : this - > loadedSubAssets )
{
if ( subAsset . type = = alias . first . type & & name = = Game : : DB_GetXAssetName ( & subAsset ) )
{
found = true ;
break ;
}
}
if ( ! found )
{
Logger : : Error ( " Asset %s of type %s was written, but not loaded! " , name . data ( ) , Game : : DB_GetXAssetTypeName ( alias . first . type ) ) ;
}
}
# endif
2016-12-24 16:12:25 -05:00
// Unload our fastfiles
Game : : XZoneInfo info ;
info . name = nullptr ;
info . allocFlags = 0 ;
info . freeFlags = 0x20 ;
Game : : DB_LoadXAssets ( & info , 1 , true ) ;
AssetHandler : : ClearTemporaryAssets ( ) ;
Localization : : ClearTemp ( ) ;
2016-07-11 11:14:58 -04:00
}
2016-11-20 08:09:07 -05:00
Utils : : Stream * ZoneBuilder : : Zone : : getBuffer ( )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
return & this - > buffer ;
2016-07-11 11:14:58 -04:00
}
2016-11-20 08:09:07 -05:00
Utils : : Memory : : Allocator * ZoneBuilder : : Zone : : getAllocator ( )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
return & this - > memAllocator ;
2016-07-11 11:14:58 -04:00
}
2016-11-20 08:09:07 -05:00
void ZoneBuilder : : Zone : : Zone : : build ( )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
this - > loadFastFiles ( ) ;
2016-07-11 11:14:58 -04:00
Logger : : Print ( " Linking assets... \n " ) ;
2016-11-20 08:09:07 -05:00
if ( ! this - > loadAssets ( ) ) return ;
2016-07-11 11:14:58 -04:00
2016-11-20 08:09:07 -05:00
this - > addBranding ( ) ;
2016-07-11 11:14:58 -04:00
Logger : : Print ( " Saving... \n " ) ;
2016-11-20 08:09:07 -05:00
this - > saveData ( ) ;
2016-07-11 11:14:58 -04:00
Logger : : Print ( " Compressing... \n " ) ;
2016-11-20 08:09:07 -05:00
this - > writeZone ( ) ;
2016-07-11 11:14:58 -04:00
}
2016-11-20 08:09:07 -05:00
void ZoneBuilder : : Zone : : loadFastFiles ( )
2016-07-11 11:14:58 -04:00
{
Logger : : Print ( " Loading required FastFiles... \n " ) ;
2016-11-20 08:09:07 -05:00
for ( int i = 0 ; i < this - > dataMap . getRows ( ) ; + + i )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
if ( this - > dataMap . getElementAt ( i , 0 ) = = " require " )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
std : : string fastfile = this - > dataMap . getElementAt ( i , 1 ) ;
2016-07-11 11:14:58 -04:00
if ( ! Game : : DB_IsZoneLoaded ( fastfile . data ( ) ) )
{
Game : : XZoneInfo info ;
info . name = fastfile . data ( ) ;
2016-10-26 13:15:18 -04:00
info . allocFlags = 0x20 ;
2016-07-11 11:14:58 -04:00
info . freeFlags = 0 ;
Game : : DB_LoadXAssets ( & info , 1 , true ) ;
}
else
{
Logger : : Print ( " Zone '%s' already loaded \n " , fastfile . data ( ) ) ;
}
}
}
}
2016-11-20 08:09:07 -05:00
bool ZoneBuilder : : Zone : : loadAssets ( )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
for ( int i = 0 ; i < this - > dataMap . getRows ( ) ; + + i )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
if ( this - > dataMap . getElementAt ( i , 0 ) ! = " require " )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
if ( this - > dataMap . getColumns ( i ) > 2 )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
if ( this - > dataMap . getElementAt ( i , 0 ) = = " localize " )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
std : : string stringOverride = this - > dataMap . getElementAt ( i , 2 ) ;
2016-07-11 11:14:58 -04:00
Utils : : String : : Replace ( stringOverride , " \\ n " , " \n " ) ;
2016-11-20 08:09:07 -05:00
Localization : : SetTemp ( this - > dataMap . getElementAt ( i , 1 ) , stringOverride ) ;
2016-07-11 11:14:58 -04:00
}
else
{
2016-11-20 08:09:07 -05:00
std : : string oldName = this - > dataMap . getElementAt ( i , 1 ) ;
std : : string newName = this - > dataMap . getElementAt ( i , 2 ) ;
std : : string typeName = this - > dataMap . getElementAt ( i , 0 ) . data ( ) ;
2016-10-26 15:50:57 -04:00
Game : : XAssetType type = Game : : DB_GetXAssetNameType ( typeName . data ( ) ) ;
if ( type < Game : : XAssetType : : ASSET_TYPE_COUNT & & type > = 0 )
{
2016-11-20 08:09:07 -05:00
this - > renameAsset ( type , oldName , newName ) ;
2016-10-26 15:50:57 -04:00
}
else
{
Logger : : Error ( " Unable to rename '%s' to '%s' as the asset type '%s' is invalid! " , oldName . data ( ) , newName . data ( ) , typeName . data ( ) ) ;
}
2016-07-11 11:14:58 -04:00
}
}
2016-12-23 15:01:56 -05:00
if ( ! this - > loadAsset ( this - > dataMap . getElementAt ( i , 0 ) , this - > dataMap . getElementAt ( i , 1 ) , false ) )
2016-07-11 11:14:58 -04:00
{
return false ;
}
}
}
return true ;
}
2016-12-23 15:01:56 -05:00
bool ZoneBuilder : : Zone : : loadAsset ( Game : : XAssetType type , void * data , bool isSubAsset )
2016-07-11 11:14:58 -04:00
{
2016-12-23 15:01:56 -05:00
Game : : XAsset asset { type , { data } } ;
2017-01-13 09:44:46 -05:00
const char * name = Game : : DB_GetXAssetName ( & asset ) ;
if ( name ) return this - > loadAsset ( type , std : : string ( name ) , isSubAsset ) ;
else return false ;
2016-07-11 11:14:58 -04:00
}
2016-12-23 15:01:56 -05:00
bool ZoneBuilder : : Zone : : loadAsset ( Game : : XAssetType type , std : : string name , bool isSubAsset )
{
return this - > loadAsset ( Game : : DB_GetXAssetTypeName ( type ) , name , isSubAsset ) ;
}
bool ZoneBuilder : : Zone : : loadAsset ( std : : string typeName , std : : string name , bool isSubAsset )
2016-07-11 11:14:58 -04:00
{
Game : : XAssetType type = Game : : DB_GetXAssetNameType ( typeName . data ( ) ) ;
2017-01-13 09:44:46 -05:00
// Sanitize name for empty assets
if ( name [ 0 ] = = ' , ' ) name . erase ( name . begin ( ) ) ;
2016-12-23 15:01:56 -05:00
if ( this - > findAsset ( type , name ) ! = - 1 | | this - > findSubAsset ( type , name ) . data ) return true ;
2016-07-11 11:14:58 -04:00
if ( type = = Game : : XAssetType : : ASSET_TYPE_INVALID | | type > = Game : : XAssetType : : ASSET_TYPE_COUNT )
{
Logger : : Error ( " Error: Invalid asset type '%s' \n " , typeName . data ( ) ) ;
return false ;
}
2017-01-13 09:44:46 -05:00
Game : : XAssetHeader assetHeader = AssetHandler : : FindAssetForZone ( type , name , this , isSubAsset ) ;
2016-07-11 11:14:58 -04:00
if ( ! assetHeader . data )
{
Logger : : Error ( " Error: Missing asset '%s' of type '%s' \n " , name . data ( ) , Game : : DB_GetXAssetTypeName ( type ) ) ;
return false ;
}
Game : : XAsset asset ;
asset . type = type ;
asset . header = assetHeader ;
2016-12-23 15:01:56 -05:00
if ( isSubAsset )
{
this - > loadedSubAssets . push_back ( asset ) ;
}
else
{
this - > loadedAssets . push_back ( asset ) ;
}
2016-12-23 01:42:56 -05:00
// Handle script strings
2016-07-11 11:14:58 -04:00
AssetHandler : : ZoneMark ( asset , this ) ;
return true ;
}
2016-11-20 08:09:07 -05:00
int ZoneBuilder : : Zone : : findAsset ( Game : : XAssetType type , std : : string name )
2016-07-11 11:14:58 -04:00
{
2017-01-13 09:44:46 -05:00
if ( name [ 0 ] = = ' , ' ) name . erase ( name . begin ( ) ) ;
2016-11-20 08:09:07 -05:00
for ( unsigned int i = 0 ; i < this - > loadedAssets . size ( ) ; + + i )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
Game : : XAsset * asset = & this - > loadedAssets [ i ] ;
2016-07-11 11:14:58 -04:00
if ( asset - > type ! = type ) continue ;
2016-12-23 15:01:56 -05:00
const char * assetName = Game : : DB_GetXAssetName ( asset ) ;
2017-01-13 09:44:46 -05:00
if ( assetName [ 0 ] = = ' , ' ) + + assetName ;
2016-07-11 11:14:58 -04:00
if ( name = = assetName )
{
return i ;
}
}
return - 1 ;
}
2016-12-23 15:01:56 -05:00
Game : : XAssetHeader ZoneBuilder : : Zone : : findSubAsset ( Game : : XAssetType type , std : : string name )
{
2017-01-13 09:44:46 -05:00
if ( name [ 0 ] = = ' , ' ) name . erase ( name . begin ( ) ) ;
2016-12-23 15:01:56 -05:00
for ( unsigned int i = 0 ; i < this - > loadedSubAssets . size ( ) ; + + i )
{
Game : : XAsset * asset = & this - > loadedSubAssets [ i ] ;
if ( asset - > type ! = type ) continue ;
const char * assetName = Game : : DB_GetXAssetName ( asset ) ;
2017-01-13 09:44:46 -05:00
if ( assetName [ 0 ] = = ' , ' ) + + assetName ;
2016-12-23 15:01:56 -05:00
if ( name = = assetName )
{
return asset - > header ;
}
}
return { 0 } ;
}
2016-11-20 08:09:07 -05:00
Game : : XAsset * ZoneBuilder : : Zone : : getAsset ( int index )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
if ( static_cast < uint32_t > ( index ) < this - > loadedAssets . size ( ) )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
return & this - > loadedAssets [ index ] ;
2016-07-11 11:14:58 -04:00
}
return nullptr ;
}
2016-12-23 01:42:56 -05:00
uint32_t ZoneBuilder : : Zone : : getAssetTableOffset ( int index )
2016-07-11 11:14:58 -04:00
{
Utils : : Stream : : Offset offset ;
offset . block = Game : : XFILE_BLOCK_VIRTUAL ;
2016-11-20 08:09:07 -05:00
offset . offset = ( this - > indexStart + ( index * sizeof ( Game : : XAsset ) ) + 4 ) ;
return offset . getPackedOffset ( ) ;
2016-07-11 11:14:58 -04:00
}
2016-12-23 01:42:56 -05:00
2016-12-23 15:01:56 -05:00
bool ZoneBuilder : : Zone : : hasAlias ( Game : : XAsset asset )
2016-12-23 01:42:56 -05:00
{
2016-12-23 15:01:56 -05:00
return this - > getAlias ( asset ) ! = 0 ;
2016-12-23 01:42:56 -05:00
}
2016-07-11 11:14:58 -04:00
2016-12-23 08:43:37 -05:00
Game : : XAssetHeader ZoneBuilder : : Zone : : saveSubAsset ( Game : : XAssetType type , void * ptr )
2016-12-23 01:42:56 -05:00
{
2016-12-23 15:01:56 -05:00
Game : : XAssetHeader header { ptr } ;
Game : : XAsset asset { type , header } ;
std : : string name = Game : : DB_GetXAssetName ( & asset ) ;
2016-12-23 01:42:56 -05:00
2016-12-23 15:01:56 -05:00
int assetIndex = this - > findAsset ( type , name ) ;
2016-12-23 01:42:56 -05:00
if ( assetIndex = = - 1 ) // nested asset
2016-07-11 11:14:58 -04:00
{
2016-12-23 01:42:56 -05:00
// already written. find alias and store in ptr
2016-12-23 15:01:56 -05:00
if ( this - > hasAlias ( asset ) )
2016-12-23 01:42:56 -05:00
{
2016-12-23 15:01:56 -05:00
header . data = reinterpret_cast < void * > ( this - > getAlias ( asset ) ) ;
2016-12-23 01:42:56 -05:00
}
else
{
2016-12-23 15:01:56 -05:00
asset . header = this - > findSubAsset ( type , name ) ;
if ( ! asset . header . data )
{
2017-01-13 09:44:46 -05:00
Logger : : Error ( " Missing required asset '%s' (%s). Export failed! " , name . data ( ) , Game : : DB_GetXAssetTypeName ( type ) ) ;
2016-12-23 15:01:56 -05:00
}
2016-12-23 01:42:56 -05:00
# ifdef DEBUG
2016-12-23 15:01:56 -05:00
Components : : Logger : : Print ( " Saving require (%s): %s \n " , Game : : DB_GetXAssetTypeName ( type ) , Game : : DB_GetXAssetNameHandlers [ type ] ( & header ) ) ;
2016-12-23 01:42:56 -05:00
# endif
2016-12-23 15:01:56 -05:00
2016-12-23 08:43:37 -05:00
// we alias the next 4 (aligned) bytes of the stream b/c DB_InsertPointer gives us a nice pointer to use as the alias
// otherwise it would be a fuckfest trying to figure out where the alias is in the stream
2016-12-23 18:03:44 -05:00
this - > buffer . pushBlock ( Game : : XFILE_BLOCK_VIRTUAL ) ;
2016-12-23 08:43:37 -05:00
this - > buffer . align ( Utils : : Stream : : ALIGN_4 ) ;
2016-12-23 15:01:56 -05:00
this - > storeAlias ( asset ) ;
2016-12-23 18:03:44 -05:00
this - > buffer . increaseBlockSize ( 4 ) ;
2016-12-23 15:01:56 -05:00
this - > buffer . popBlock ( ) ;
2016-12-23 01:42:56 -05:00
this - > buffer . pushBlock ( Game : : XFILE_BLOCK_TEMP ) ;
2016-12-23 18:03:44 -05:00
this - > buffer . align ( Utils : : Stream : : ALIGN_4 ) ;
2016-12-23 15:01:56 -05:00
AssetHandler : : ZoneSave ( asset , this ) ;
2016-12-23 01:42:56 -05:00
this - > buffer . popBlock ( ) ;
2016-12-23 15:01:56 -05:00
2016-12-23 08:43:37 -05:00
header . data = reinterpret_cast < void * > ( - 2 ) ; // DB_InsertPointer marker
2016-12-23 01:42:56 -05:00
}
2016-07-11 11:14:58 -04:00
}
else
{
2016-12-23 01:42:56 -05:00
// asset was written normally. not sure this is even possible but its here
header . data = reinterpret_cast < void * > ( this - > getAssetTableOffset ( assetIndex ) ) ;
2016-07-11 11:14:58 -04:00
}
return header ;
}
2016-11-20 08:09:07 -05:00
void ZoneBuilder : : Zone : : writeZone ( )
2016-07-11 11:14:58 -04:00
{
FILETIME fileTime ;
GetSystemTimeAsFileTime ( & fileTime ) ;
2016-12-22 09:21:09 -05:00
Game : : XFileHeader header =
{
# ifdef DEBUG
2017-01-01 10:45:30 -05:00
XFILE_MAGIC_UNSIGNED ,
2016-12-22 09:21:09 -05:00
# else
2017-01-01 10:45:30 -05:00
XFILE_HEADER_IW4X | ( static_cast < unsigned __int64 > ( XFILE_VERSION_IW4X ) < < 32 ) ,
2016-12-22 09:21:09 -05:00
# endif
2017-01-01 10:45:30 -05:00
XFILE_VERSION ,
2016-12-22 09:21:09 -05:00
Game : : XFileLanguage : : XLANG_NONE ,
fileTime . dwHighDateTime ,
fileTime . dwLowDateTime
} ;
2016-07-11 11:14:58 -04:00
std : : string outBuffer ;
outBuffer . append ( reinterpret_cast < char * > ( & header ) , sizeof ( header ) ) ;
2016-11-20 08:09:07 -05:00
std : : string zoneBuffer = this - > buffer . toBuffer ( ) ;
2017-01-01 10:45:30 -05:00
# ifndef DEBUG
// Insert a random byte, this will destroy the whole alignment and result in a crash, if not handled
zoneBuffer . insert ( zoneBuffer . begin ( ) , static_cast < char > ( Utils : : Cryptography : : Rand : : GenerateInt ( ) ) ) ;
# endif
2016-07-11 11:14:58 -04:00
zoneBuffer = Utils : : Compression : : ZLib : : Compress ( zoneBuffer ) ;
outBuffer . append ( zoneBuffer ) ;
2016-11-20 08:09:07 -05:00
std : : string outFile = " zone/ " + this - > zoneName + " .ff " ;
2016-07-11 11:14:58 -04:00
Utils : : IO : : WriteFile ( outFile , outBuffer ) ;
Logger : : Print ( " done. \n " ) ;
2016-12-23 15:01:56 -05:00
Logger : : Print ( " Zone '%s' written with %d assets and %d script strings \n " , outFile . data ( ) , ( this - > aliasList . size ( ) + this - > loadedAssets . size ( ) ) , this - > scriptStrings . size ( ) ) ;
2016-07-11 11:14:58 -04:00
}
2016-11-20 08:09:07 -05:00
void ZoneBuilder : : Zone : : saveData ( )
2016-07-11 11:14:58 -04:00
{
// Add header
Game : : ZoneHeader zoneHeader = { 0 } ;
2016-11-20 08:09:07 -05:00
zoneHeader . assetList . assetCount = this - > loadedAssets . size ( ) ;
2016-07-11 11:14:58 -04:00
Utils : : Stream : : ClearPointer ( & zoneHeader . assetList . assets ) ;
// Increment ScriptStrings count (for empty script string) if available
2016-11-20 08:09:07 -05:00
if ( ! this - > scriptStrings . empty ( ) )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
zoneHeader . assetList . stringList . count = this - > scriptStrings . size ( ) + 1 ;
2016-07-11 11:14:58 -04:00
Utils : : Stream : : ClearPointer ( & zoneHeader . assetList . stringList . strings ) ;
}
// Write header
2016-11-20 08:09:07 -05:00
this - > buffer . save ( & zoneHeader , sizeof ( Game : : ZoneHeader ) ) ;
this - > buffer . pushBlock ( Game : : XFILE_BLOCK_VIRTUAL ) ; // Push main stream onto the stream stack
2016-07-11 11:14:58 -04:00
2016-11-20 08:09:07 -05:00
// Write ScriptStrings, if available
if ( ! this - > scriptStrings . empty ( ) )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
this - > buffer . saveNull ( 4 ) ; // Empty script string?
2016-12-23 01:42:56 -05:00
// This actually represents a NULL string, but as scriptString.
2016-11-20 08:09:07 -05:00
// So scriptString loading for NULL scriptStrings from fastfile results in a NULL scriptString.
// That's the reason why the count is incremented by 1, if scriptStrings are available.
2016-07-11 11:14:58 -04:00
// Write ScriptString pointer table
2016-11-20 08:09:07 -05:00
for ( size_t i = 0 ; i < this - > scriptStrings . size ( ) ; + + i )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
this - > buffer . saveMax ( 4 ) ;
2016-07-11 11:14:58 -04:00
}
2016-11-20 08:09:07 -05:00
this - > buffer . align ( Utils : : Stream : : ALIGN_4 ) ;
2016-07-11 11:14:58 -04:00
// Write ScriptStrings
2016-11-20 08:09:07 -05:00
for ( auto ScriptString : this - > scriptStrings )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
this - > buffer . saveString ( ScriptString . data ( ) ) ;
2016-07-11 11:14:58 -04:00
}
}
// Align buffer (4 bytes) to get correct offsets for pointers
2016-11-20 08:09:07 -05:00
this - > buffer . align ( Utils : : Stream : : ALIGN_4 ) ;
this - > indexStart = this - > buffer . getBlockSize ( Game : : XFILE_BLOCK_VIRTUAL ) ; // Mark AssetTable offset
2016-07-11 11:14:58 -04:00
// AssetTable
2016-11-20 08:09:07 -05:00
for ( auto asset : this - > loadedAssets )
2016-07-11 11:14:58 -04:00
{
Game : : XAsset entry = { asset . type , 0 } ;
Utils : : Stream : : ClearPointer ( & entry . header . data ) ;
2016-11-20 08:09:07 -05:00
this - > buffer . save ( & entry ) ;
2016-07-11 11:14:58 -04:00
}
// Assets
2016-11-20 08:09:07 -05:00
for ( auto asset : this - > loadedAssets )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
this - > buffer . pushBlock ( Game : : XFILE_BLOCK_TEMP ) ;
this - > buffer . align ( Utils : : Stream : : ALIGN_4 ) ;
2016-07-11 11:14:58 -04:00
# ifdef DEBUG
2016-12-23 01:42:56 -05:00
Components : : Logger : : Print ( " Saving (%s): %s \n " , Game : : DB_GetXAssetTypeName ( asset . type ) , Game : : DB_GetXAssetNameHandlers [ asset . type ] ( & asset . header ) ) ;
2016-07-11 11:14:58 -04:00
# endif
2016-11-20 08:09:07 -05:00
this - > store ( asset . header ) ;
2016-07-11 11:14:58 -04:00
AssetHandler : : ZoneSave ( asset , this ) ;
2016-11-20 08:09:07 -05:00
this - > buffer . popBlock ( ) ;
2016-07-11 11:14:58 -04:00
}
// Adapt header
2016-11-20 08:09:07 -05:00
this - > buffer . enterCriticalSection ( ) ;
Game : : XFile * header = reinterpret_cast < Game : : XFile * > ( this - > buffer . data ( ) ) ;
header - > size = this - > buffer . length ( ) - sizeof ( Game : : XFile ) ; // Write correct data size
2016-12-21 20:48:00 -05:00
header - > externalSize = this - > externalSize ; // This actually stores how much external data has to be loaded. It's used to calculate the loadscreen progress
2016-07-11 11:14:58 -04:00
// Write stream sizes
for ( int i = 0 ; i < Game : : MAX_XFILE_COUNT ; + + i )
{
2016-11-20 08:09:07 -05:00
header - > blockSize [ i ] = this - > buffer . getBlockSize ( static_cast < Game : : XFILE_BLOCK_TYPES > ( i ) ) ;
2016-07-11 11:14:58 -04:00
}
2016-11-20 08:09:07 -05:00
this - > buffer . leaveCriticalSection ( ) ;
this - > buffer . popBlock ( ) ;
2016-07-11 11:14:58 -04:00
}
// Add branding asset
2016-11-20 08:09:07 -05:00
void ZoneBuilder : : Zone : : addBranding ( )
2016-07-11 11:14:58 -04:00
{
char * data = " FastFile built using IW4x ZoneTool! " ;
2016-11-20 08:09:07 -05:00
this - > branding = { this - > zoneName . data ( ) , ( int ) strlen ( data ) , 0 , data } ;
2016-07-11 11:14:58 -04:00
2016-11-20 08:09:07 -05:00
if ( this - > findAsset ( Game : : XAssetType : : ASSET_TYPE_RAWFILE , this - > branding . name ) ! = - 1 )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
Logger : : Error ( " Unable to add branding. Asset '%s' already exists! " , this - > branding . name ) ;
2016-07-11 11:14:58 -04:00
}
2016-11-20 08:09:07 -05:00
Game : : XAssetHeader header = { & this - > branding } ;
2016-07-11 11:14:58 -04:00
Game : : XAsset brandingAsset = { Game : : XAssetType : : ASSET_TYPE_RAWFILE , header } ;
2016-11-20 08:09:07 -05:00
this - > loadedAssets . push_back ( brandingAsset ) ;
2016-07-11 11:14:58 -04:00
}
// Check if the given pointer has already been mapped
2016-11-20 08:09:07 -05:00
bool ZoneBuilder : : Zone : : hasPointer ( const void * pointer )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
return ( this - > pointerMap . find ( pointer ) ! = this - > pointerMap . end ( ) ) ;
2016-07-11 11:14:58 -04:00
}
// Get stored offset for given file pointer
2016-12-23 01:42:56 -05:00
unsigned int ZoneBuilder : : Zone : : safeGetPointer ( const void * pointer )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
if ( this - > hasPointer ( pointer ) )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
return this - > pointerMap [ pointer ] ;
2016-07-11 11:14:58 -04:00
}
return NULL ;
}
2016-12-23 08:43:37 -05:00
void ZoneBuilder : : Zone : : storePointer ( const void * pointer )
{
this - > pointerMap [ pointer ] = this - > buffer . getPackedOffset ( ) ;
}
2016-12-23 15:01:56 -05:00
void ZoneBuilder : : Zone : : storeAlias ( Game : : XAsset asset )
2016-12-23 01:42:56 -05:00
{
2016-12-23 15:01:56 -05:00
if ( ! this - > hasAlias ( asset ) )
{
this - > aliasList . push_back ( { asset , this - > buffer . getPackedOffset ( ) } ) ;
}
2016-12-23 01:42:56 -05:00
}
2016-12-23 15:01:56 -05:00
unsigned int ZoneBuilder : : Zone : : getAlias ( Game : : XAsset asset )
2016-07-11 11:14:58 -04:00
{
2016-12-23 15:01:56 -05:00
std : : string name = Game : : DB_GetXAssetName ( & asset ) ;
for ( auto & entry : this - > aliasList )
2016-12-23 01:42:56 -05:00
{
2016-12-23 15:01:56 -05:00
if ( asset . type = = entry . first . type & & name = = Game : : DB_GetXAssetName ( & entry . first ) )
{
return entry . second ;
}
2016-12-23 01:42:56 -05:00
}
2016-12-23 15:01:56 -05:00
2016-12-23 01:42:56 -05:00
return 0 ;
2016-07-11 11:14:58 -04:00
}
2016-11-20 08:09:07 -05:00
int ZoneBuilder : : Zone : : addScriptString ( std : : string str )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
return this - > addScriptString ( Game : : SL_GetString ( str . data ( ) , 0 ) ) ;
2016-07-11 11:14:58 -04:00
}
// Mark a scriptString for writing and map it.
2016-11-20 08:09:07 -05:00
int ZoneBuilder : : Zone : : addScriptString ( unsigned short gameIndex )
2016-07-11 11:14:58 -04:00
{
// Handle NULL scriptStrings
// Might optimize that later
if ( ! gameIndex )
{
2016-11-20 08:09:07 -05:00
if ( this - > scriptStrings . empty ( ) )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
this - > scriptStrings . push_back ( " " ) ;
2016-07-11 11:14:58 -04:00
}
return 0 ;
}
std : : string str = Game : : SL_ConvertToString ( gameIndex ) ;
2016-11-20 08:09:07 -05:00
int prev = this - > findScriptString ( str ) ;
2016-07-11 11:14:58 -04:00
if ( prev > 0 )
{
2016-11-20 08:09:07 -05:00
this - > scriptStringMap [ gameIndex ] = prev ;
2016-07-11 11:14:58 -04:00
return prev ;
}
2016-11-20 08:09:07 -05:00
this - > scriptStrings . push_back ( str ) ;
this - > scriptStringMap [ gameIndex ] = this - > scriptStrings . size ( ) ;
return this - > scriptStrings . size ( ) ;
2016-07-11 11:14:58 -04:00
}
// Find a local scriptString
2016-11-20 08:09:07 -05:00
int ZoneBuilder : : Zone : : findScriptString ( std : : string str )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
for ( unsigned int i = 0 ; i < this - > scriptStrings . size ( ) ; + + i )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
if ( this - > scriptStrings [ i ] = = str )
2016-07-11 11:14:58 -04:00
{
return ( i + 1 ) ;
}
}
return - 1 ;
}
// Remap a scriptString to it's corresponding value in the local scriptString table.
2016-11-20 08:09:07 -05:00
void ZoneBuilder : : Zone : : mapScriptString ( unsigned short * gameIndex )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
* gameIndex = 0xFFFF & this - > scriptStringMap [ * gameIndex ] ;
2016-07-11 11:14:58 -04:00
}
// Store a new name for a given asset
2016-11-20 08:09:07 -05:00
void ZoneBuilder : : Zone : : renameAsset ( Game : : XAssetType type , std : : string asset , std : : string newName )
2016-07-11 11:14:58 -04:00
{
2016-10-26 15:50:57 -04:00
if ( type < Game : : XAssetType : : ASSET_TYPE_COUNT & & type > = 0 )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
this - > renameMap [ type ] [ asset ] = newName ;
2016-07-11 11:14:58 -04:00
}
2016-10-26 15:50:57 -04:00
else
{
Logger : : Error ( " Unable to rename '%s' to '%s' as the asset type is invalid! " , asset . data ( ) , newName . data ( ) ) ;
}
2016-07-11 11:14:58 -04:00
}
// Return the new name for a given asset
2016-11-20 08:09:07 -05:00
std : : string ZoneBuilder : : Zone : : getAssetName ( Game : : XAssetType type , std : : string asset )
2016-07-11 11:14:58 -04:00
{
2016-10-26 15:50:57 -04:00
if ( type < Game : : XAssetType : : ASSET_TYPE_COUNT & & type > = 0 )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
if ( this - > renameMap [ type ] . find ( asset ) ! = this - > renameMap [ type ] . end ( ) )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
return this - > renameMap [ type ] [ asset ] ;
2016-07-11 11:14:58 -04:00
}
}
2016-10-26 15:50:57 -04:00
else
{
Logger : : Error ( " Unable to get name for '%s' as the asset type is invalid! " , asset . data ( ) ) ;
}
2016-07-11 11:14:58 -04:00
return asset ;
}
2016-11-20 08:09:07 -05:00
void ZoneBuilder : : Zone : : store ( Game : : XAssetHeader header )
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
if ( ! this - > hasPointer ( header . data ) ) // We should never have to restore a pointer, so this expression should always resolve into false
2016-07-11 11:14:58 -04:00
{
2016-11-20 08:09:07 -05:00
this - > storePointer ( header . data ) ;
2016-07-11 11:14:58 -04:00
}
}
2016-11-20 14:41:38 -05:00
void ZoneBuilder : : Zone : : incrementExternalSize ( unsigned int size )
{
this - > externalSize + = size ;
}
2016-07-11 11:14:58 -04:00
bool ZoneBuilder : : IsEnabled ( )
{
2016-07-22 06:12:11 -04:00
return ( Flags : : HasFlag ( " zonebuilder " ) & & ! Dedicated : : IsEnabled ( ) ) ;
2016-07-11 11:14:58 -04:00
}
void ZoneBuilder : : BeginAssetTrace ( std : : string zone )
{
ZoneBuilder : : TraceZone = zone ;
}
std : : vector < std : : pair < Game : : XAssetType , std : : string > > ZoneBuilder : : EndAssetTrace ( )
{
ZoneBuilder : : TraceZone . clear ( ) ;
std : : vector < std : : pair < Game : : XAssetType , std : : string > > AssetTrace ;
Utils : : Merge ( & AssetTrace , ZoneBuilder : : TraceAssets ) ;
ZoneBuilder : : TraceAssets . clear ( ) ;
return AssetTrace ;
}
2017-01-13 09:44:46 -05:00
Game : : XAssetHeader ZoneBuilder : : GetEmptyAssetIfCommon ( Game : : XAssetType type , std : : string name , ZoneBuilder : : Zone * builder )
{
Game : : XAssetHeader header = { 0 } ;
if ( type > = 0 & & type < Game : : XAssetType : : ASSET_TYPE_COUNT )
{
for ( auto & asset : ZoneBuilder : : CommonAssets )
{
if ( asset . first = = type & & asset . second = = name )
{
2017-01-13 10:01:13 -05:00
// Allocate an empty asset (filled with zeros)
2017-01-13 09:44:46 -05:00
header . data = builder - > getAllocator ( ) - > allocate ( Game : : DB_GetXAssetSizeHandlers [ type ] ( ) ) ;
// Set the name to the original name, so it can be stored
Game : : DB_SetXAssetNameHandlers [ type ] ( & header , name . data ( ) ) ;
AssetHandler : : StoreTemporaryAsset ( type , header ) ;
// Set the name to the empty name
Game : : DB_SetXAssetNameHandlers [ type ] ( & header , builder - > getAllocator ( ) - > duplicateString ( " , " + name ) ) ;
break ;
}
}
}
return header ;
}
2016-12-21 20:48:00 -05:00
int ZoneBuilder : : StoreTexture ( Game : : GfxImageLoadDef * * loadDef , Game : : GfxImage * image )
{
size_t size = 16 + ( * loadDef ) - > resourceSize ;
void * data = ZoneBuilder : : MemAllocator . allocate ( size ) ;
std : : memcpy ( data , * loadDef , size ) ;
image - > loadDef = reinterpret_cast < Game : : GfxImageLoadDef * > ( data ) ;
return 0 ;
}
void ZoneBuilder : : ReleaseTexture ( Game : : XAssetHeader header )
{
if ( header . image & & header . image - > loadDef )
{
ZoneBuilder : : MemAllocator . free ( header . image - > loadDef ) ;
}
}
2016-07-11 11:14:58 -04:00
ZoneBuilder : : ZoneBuilder ( )
{
2016-11-20 08:09:07 -05:00
AssertSize ( Game : : XFileHeader , 21 ) ;
AssertSize ( Game : : XFile , 40 ) ;
2016-07-11 11:14:58 -04:00
static_assert ( Game : : MAX_XFILE_COUNT = = 8 , " XFile block enum is invalid! " ) ;
ZoneBuilder : : EndAssetTrace ( ) ;
2016-12-23 01:42:56 -05:00
2016-07-11 11:14:58 -04:00
if ( ZoneBuilder : : IsEnabled ( ) )
{
// Prevent loading textures (preserves loaddef)
2016-12-21 20:48:00 -05:00
//Utils::Hook::Set<BYTE>(Game::Load_Texture, 0xC3);
// Store the loaddef
Utils : : Hook ( Game : : Load_Texture , StoreTexture , HOOK_JUMP ) . install ( ) - > quick ( ) ;
// Release the loaddef
Game : : DB_ReleaseXAssetHandlers [ Game : : XAssetType : : ASSET_TYPE_IMAGE ] = ZoneBuilder : : ReleaseTexture ;
2016-07-11 11:14:58 -04:00
2016-12-23 01:42:56 -05:00
//r_loadForrenderer = 0
2016-07-11 11:14:58 -04:00
Utils : : Hook : : Set < BYTE > ( 0x519DDF , 0 ) ;
//r_delayloadimage retn
Utils : : Hook : : Set < BYTE > ( 0x51F450 , 0xC3 ) ;
// r_registerDvars hack
Utils : : Hook : : Set < BYTE > ( 0x51B1CD , 0xC3 ) ;
// Prevent destroying textures
Utils : : Hook : : Set < BYTE > ( 0x51F03D , 0xEB ) ;
// Don't create default assets
Utils : : Hook : : Set < BYTE > ( 0x407BAA , 0xEB ) ;
2016-12-22 17:21:20 -05:00
// Don't mark clip maps as 'in use'
Utils : : Hook : : Nop ( 0x405E07 , 7 ) ;
// Don't mark assets
//Utils::Hook::Nop(0x5BB632, 5);
2016-11-29 13:32:46 -05:00
// Don't load sounds
2016-11-29 15:33:31 -05:00
//Utils::Hook::Set<BYTE>(0x413430, 0xC3);
2016-11-29 13:32:46 -05:00
2016-07-11 11:14:58 -04:00
// Don't display errors when assets are missing (we might manually build those)
Utils : : Hook : : Nop ( 0x5BB3F2 , 5 ) ;
Utils : : Hook : : Nop ( 0x5BB422 , 5 ) ;
Utils : : Hook : : Nop ( 0x5BB43A , 5 ) ;
2016-10-21 16:50:17 -04:00
2016-07-11 11:14:58 -04:00
// Increase asset pools
Game : : ReallocateAssetPool ( Game : : XAssetType : : ASSET_TYPE_MAP_ENTS , 10 ) ;
2016-10-21 16:50:17 -04:00
Game : : ReallocateAssetPool ( Game : : XAssetType : : ASSET_TYPE_XMODELSURFS , 8192 ) ;
Game : : ReallocateAssetPool ( Game : : XAssetType : : ASSET_TYPE_IMAGE , 14336 ) ;
2016-12-28 15:35:30 -05:00
Game : : ReallocateAssetPool ( Game : : XAssetType : : ASSET_TYPE_TECHNIQUE_SET , 1536 ) ;
2016-07-11 11:14:58 -04:00
// hunk size (was 300 MiB)
Utils : : Hook : : Set < DWORD > ( 0x64A029 , 0x38400000 ) ; // 900 MiB
Utils : : Hook : : Set < DWORD > ( 0x64A057 , 0x38400000 ) ;
2016-08-17 20:18:45 -04:00
AssetHandler : : OnLoad ( [ ] ( Game : : XAssetType type , Game : : XAssetHeader /*asset*/ , std : : string name , bool * /*restrict*/ )
2016-07-11 11:14:58 -04:00
{
2017-01-13 09:44:46 -05:00
// This is used to track which assets can be stored as empty assets
if ( FastFiles : : Current ( ) = = " common_mp " )
{
ZoneBuilder : : CommonAssets . push_back ( { type , name } ) ;
}
2016-07-11 11:14:58 -04:00
if ( ! ZoneBuilder : : TraceZone . empty ( ) & & ZoneBuilder : : TraceZone = = FastFiles : : Current ( ) )
{
ZoneBuilder : : TraceAssets . push_back ( { type , name } ) ;
}
} ) ;
2016-12-26 15:00:59 -05:00
Command : : Add ( " verifyzone " , [ ] ( Command : : Params * params )
2016-07-11 11:14:58 -04:00
{
2016-12-07 14:00:24 -05:00
if ( params - > length ( ) < 2 ) return ;
2016-07-11 11:14:58 -04:00
2016-12-07 14:00:24 -05:00
std : : string zone = params - > get ( 1 ) ;
2016-07-11 11:14:58 -04:00
ZoneBuilder : : BeginAssetTrace ( zone ) ;
Game : : XZoneInfo info ;
info . name = zone . data ( ) ;
2016-10-26 13:15:18 -04:00
info . allocFlags = 0x20 ;
2016-07-11 11:14:58 -04:00
info . freeFlags = 0 ;
Logger : : Print ( " Loading zone '%s'... \n " , zone . data ( ) ) ;
Game : : DB_LoadXAssets ( & info , 1 , true ) ;
AssetHandler : : FindOriginalAsset ( Game : : XAssetType : : ASSET_TYPE_RAWFILE , zone . data ( ) ) ; // Lock until zone is loaded
auto assets = ZoneBuilder : : EndAssetTrace ( ) ;
Logger : : Print ( " Unloading zone '%s'... \n " , zone . data ( ) ) ;
2016-10-26 13:15:18 -04:00
info . freeFlags = 0x20 ;
2016-07-11 11:14:58 -04:00
info . allocFlags = 0 ;
info . name = nullptr ;
Game : : DB_LoadXAssets ( & info , 1 , true ) ;
AssetHandler : : FindOriginalAsset ( Game : : XAssetType : : ASSET_TYPE_RAWFILE , " default " ) ; // Lock until zone is unloaded
2016-12-23 01:42:56 -05:00
2016-07-11 11:14:58 -04:00
Logger : : Print ( " Zone '%s' loaded with %d assets: \n " , zone . data ( ) , assets . size ( ) ) ;
2016-12-23 01:42:56 -05:00
2016-07-11 11:14:58 -04:00
int count = 0 ;
for ( auto i = assets . begin ( ) ; i ! = assets . end ( ) ; + + i , + + count )
{
Logger : : Print ( " %d: %s: %s \n " , count , Game : : DB_GetXAssetTypeName ( i - > first ) , i - > second . data ( ) ) ;
}
Logger : : Print ( " \n " ) ;
} ) ;
2016-12-26 15:00:59 -05:00
Command : : Add ( " buildzone " , [ ] ( Command : : Params * params )
2016-07-11 11:14:58 -04:00
{
2016-12-07 14:00:24 -05:00
if ( params - > length ( ) < 2 ) return ;
2016-07-11 11:14:58 -04:00
2016-12-07 14:00:24 -05:00
std : : string zoneName = params - > get ( 1 ) ;
2016-07-11 11:14:58 -04:00
Logger : : Print ( " Building zone '%s'... \n " , zoneName . data ( ) ) ;
2016-11-20 08:09:07 -05:00
Zone ( zoneName ) . build ( ) ;
2016-07-11 11:14:58 -04:00
} ) ;
2016-12-26 15:00:59 -05:00
Command : : Add ( " buildall " , [ ] ( Command : : Params * )
2016-11-13 14:53:44 -05:00
{
2016-11-20 08:09:07 -05:00
auto zoneSources = FileSystem : : GetSysFileList ( Dvar : : Var ( " fs_basepath " ) . get < std : : string > ( ) + " \\ zone_source " , " csv " , false ) ;
2016-11-13 14:53:44 -05:00
for ( auto source : zoneSources )
{
if ( Utils : : String : : EndsWith ( source , " .csv " ) )
{
source = source . substr ( 0 , source . find ( " .csv " ) ) ;
}
2017-01-06 09:27:35 -05:00
Command : : Execute ( Utils : : String : : VA ( " buildzone %s " , source . data ( ) ) , true ) ;
2016-11-13 14:53:44 -05:00
}
} ) ;
2016-12-26 15:00:59 -05:00
Command : : Add ( " listassets " , [ ] ( Command : : Params * params )
2016-07-11 11:14:58 -04:00
{
2016-12-07 14:00:24 -05:00
if ( params - > length ( ) < 2 ) return ;
Game : : XAssetType type = Game : : DB_GetXAssetNameType ( params - > get ( 1 ) ) ;
2016-07-11 11:14:58 -04:00
if ( type ! = Game : : XAssetType : : ASSET_TYPE_INVALID )
{
2016-12-26 15:00:59 -05:00
Game : : DB_EnumXAssets ( type , [ ] ( Game : : XAssetHeader header , void * data )
2016-07-11 11:14:58 -04:00
{
Game : : XAsset asset = { * reinterpret_cast < Game : : XAssetType * > ( data ) , header } ;
Logger : : Print ( " %s \n " , Game : : DB_GetXAssetName ( & asset ) ) ;
} , & type , false ) ;
}
} ) ;
2016-12-26 12:44:33 -05:00
2016-12-26 15:00:59 -05:00
Command : : Add ( " materialInfoDump " , [ ] ( Command : : Params * )
2016-12-26 12:44:33 -05:00
{
2016-12-26 15:00:59 -05:00
Game : : DB_EnumXAssets ( Game : : ASSET_TYPE_MATERIAL , [ ] ( Game : : XAssetHeader header , void * )
{
Logger : : Print ( " %s: %X %X %X \n " , header . material - > name , header . material - > sortKey & 0xFF , header . material - > gameFlags & 0xFF , header . material - > stateFlags & 0xFF ) ;
} , nullptr , false ) ;
2016-12-26 12:44:33 -05:00
} ) ;
2016-07-11 11:14:58 -04:00
}
}
2016-12-21 20:48:00 -05:00
ZoneBuilder : : ~ ZoneBuilder ( )
{
assert ( ZoneBuilder : : MemAllocator . empty ( ) ) ;
2017-01-13 09:44:46 -05:00
ZoneBuilder : : CommonAssets . clear ( ) ;
2016-12-21 20:48:00 -05:00
}
2016-07-11 11:14:58 -04:00
}