2022-02-27 07:53:44 -05:00
# include <STDInclude.hpp>
2017-01-19 16:23:59 -05:00
namespace Components
{
2019-10-03 13:23:06 -04:00
std : : vector < std : : string > Menus : : CustomMenus ;
std : : unordered_map < std : : string , Game : : menuDef_t * > Menus : : MenuList ;
std : : unordered_map < std : : string , Game : : MenuList * > Menus : : MenuListList ;
2017-01-19 16:23:59 -05:00
2022-12-14 06:31:03 -05:00
Game : : KeywordHashEntry < Game : : menuDef_t , 128 , 3523 > * * menuParseKeywordHash ;
template < int HASH_COUNT , int HASH_SEED >
static int KeywordHashKey ( const char * keyword )
{
auto hash = 0 ;
for ( auto i = 0 ; keyword [ i ] ; + + i )
{
hash + = ( i + HASH_SEED ) * std : : tolower ( static_cast < unsigned char > ( keyword [ i ] ) ) ;
}
return ( hash + ( hash > > 8 ) ) & ( 128 - 1 ) ;
}
template < typename T , int N , int M >
static Game : : KeywordHashEntry < T , N , M > * KeywordHashFind ( Game : : KeywordHashEntry < T , N , M > * * table , const char * keyword )
{
auto hash = KeywordHashKey < N , M > ( keyword ) ;
Game : : KeywordHashEntry < T , N , M > * key = table [ hash ] ;
if ( key & & ! _stricmp ( key - > keyword , keyword ) )
{
return key ;
}
return nullptr ;
}
2017-01-19 16:23:59 -05:00
int Menus : : ReserveSourceHandle ( )
{
// Check if a free slot is available
int i = 1 ;
for ( ; i < MAX_SOURCEFILES ; + + i )
{
if ( ! Game : : sourceFiles [ i ] )
break ;
}
if ( i > = MAX_SOURCEFILES )
return 0 ;
// Reserve it, if yes
2017-01-20 16:41:03 -05:00
Game : : sourceFiles [ i ] = reinterpret_cast < Game : : source_t * > ( 1 ) ;
2017-01-19 16:23:59 -05:00
return i ;
}
2018-12-17 08:29:18 -05:00
Game : : script_t * Menus : : LoadMenuScript ( const std : : string & name , const std : : string & buffer )
2017-01-19 16:23:59 -05:00
{
2022-12-14 06:31:03 -05:00
auto * script = static_cast < Game : : script_t * > ( Game : : GetClearedMemory ( sizeof ( Game : : script_t ) + 1 + buffer . length ( ) ) ) ;
2017-01-22 13:05:50 -05:00
if ( ! script ) return nullptr ;
2017-01-19 16:23:59 -05:00
strcpy_s ( script - > filename , sizeof ( script - > filename ) , name . data ( ) ) ;
script - > buffer = reinterpret_cast < char * > ( script + 1 ) ;
* ( script - > buffer + buffer . length ( ) ) = ' \0 ' ;
script - > script_p = script - > buffer ;
script - > lastscript_p = script - > buffer ;
2022-12-14 06:31:03 -05:00
script - > length = static_cast < int > ( buffer . length ( ) ) ;
2017-01-19 16:23:59 -05:00
script - > end_p = & script - > buffer [ buffer . length ( ) ] ;
script - > line = 1 ;
script - > lastline = 1 ;
script - > tokenavailable = 0 ;
2022-12-14 06:31:03 -05:00
Game : : PS_CreatePunctuationTable ( script , Game : : default_punctuations ) ;
script - > punctuations = Game : : default_punctuations ;
2017-01-19 16:23:59 -05:00
std : : memcpy ( script - > buffer , buffer . data ( ) , script - > length + 1 ) ;
2022-12-14 06:31:03 -05:00
script - > length = Game : : Com_Compress ( script - > buffer ) ;
2017-01-19 16:23:59 -05:00
return script ;
}
2018-12-17 08:29:18 -05:00
int Menus : : LoadMenuSource ( const std : : string & name , const std : : string & buffer )
2017-01-19 16:23:59 -05:00
{
2017-06-02 09:36:20 -04:00
Utils : : Memory : : Allocator * allocator = Utils : : Memory : : GetAllocator ( ) ;
2017-04-16 05:47:57 -04:00
2022-12-14 06:31:03 -05:00
const auto handle = ReserveSourceHandle ( ) ;
if ( ! IsValidSourceHandle ( handle ) ) return 0 ; // No free source slot!
2017-01-19 16:23:59 -05:00
2022-12-14 06:31:03 -05:00
auto * script = LoadMenuScript ( name , buffer ) ;
2017-01-19 16:23:59 -05:00
if ( ! script )
{
Game : : sourceFiles [ handle ] = nullptr ; // Free reserved slot
return 0 ;
}
2022-04-15 05:16:22 -04:00
auto * source = allocator - > allocate < Game : : source_t > ( ) ;
2017-01-19 16:23:59 -05:00
if ( ! source )
{
Game : : FreeMemory ( script ) ;
return 0 ;
}
2022-12-14 06:31:03 -05:00
std : : memset ( source , 0 , sizeof ( Game : : source_s ) ) ;
script - > next = nullptr ;
strncpy_s ( source - > filename , " string " , _TRUNCATE ) ;
2017-01-19 16:23:59 -05:00
source - > scriptstack = script ;
2017-01-20 16:41:03 -05:00
source - > tokens = nullptr ;
source - > defines = nullptr ;
source - > indentstack = nullptr ;
2017-01-19 16:23:59 -05:00
source - > skip = 0 ;
2022-12-14 06:31:03 -05:00
source - > definehash = static_cast < Game : : define_s * * > ( Game : : GetClearedMemory ( 1024 * sizeof ( Game : : define_s * ) ) ) ;
2017-01-19 16:23:59 -05:00
Game : : sourceFiles [ handle ] = source ;
return handle ;
}
bool Menus : : IsValidSourceHandle ( int handle )
{
return ( handle > 0 & & handle < MAX_SOURCEFILES & & Game : : sourceFiles [ handle ] ) ;
}
Game : : menuDef_t * Menus : : ParseMenu ( int handle )
{
2017-06-02 09:36:20 -04:00
Utils : : Memory : : Allocator * allocator = Utils : : Memory : : GetAllocator ( ) ;
2022-04-15 05:16:22 -04:00
auto * menu = allocator - > allocate < Game : : menuDef_t > ( ) ;
2017-01-19 16:23:59 -05:00
if ( ! menu ) return nullptr ;
2018-05-09 08:33:52 -04:00
menu - > items = allocator - > allocateArray < Game : : itemDef_s * > ( 512 ) ;
2017-01-19 16:23:59 -05:00
if ( ! menu - > items )
{
2017-04-16 05:47:57 -04:00
allocator - > free ( menu ) ;
2017-01-19 16:23:59 -05:00
return nullptr ;
}
Game : : pc_token_t token ;
if ( ! Game : : PC_ReadTokenHandle ( handle , & token ) | | token . string [ 0 ] ! = ' { ' )
{
2017-04-16 05:47:57 -04:00
allocator - > free ( menu - > items ) ;
allocator - > free ( menu ) ;
2017-01-19 16:23:59 -05:00
return nullptr ;
}
while ( true )
{
ZeroMemory ( & token , sizeof ( token ) ) ;
if ( ! Game : : PC_ReadTokenHandle ( handle , & token ) )
{
Game : : PC_SourceError ( handle , " end of file inside menu \n " ) ;
break ; // Fail
}
if ( * token . string = = ' } ' )
{
break ; // Success
}
2022-12-14 06:31:03 -05:00
auto * key = KeywordHashFind ( menuParseKeywordHash , token . string ) ;
2017-01-19 16:23:59 -05:00
if ( ! key )
{
Game : : PC_SourceError ( handle , " unknown menu keyword %s " , token . string ) ;
continue ;
}
if ( ! key - > func ( menu , handle ) )
{
Game : : PC_SourceError ( handle , " couldn't parse menu keyword %s " , token . string ) ;
break ; // Fail
}
}
2022-11-28 13:06:42 -05:00
if ( ! menu - > window . name )
{
2022-12-14 06:31:03 -05:00
Game : : PC_SourceError ( handle , " menu has no name " ) ;
2022-11-28 13:06:42 -05:00
allocator - > free ( menu - > items ) ;
allocator - > free ( menu ) ;
return nullptr ;
}
2022-12-14 06:31:03 -05:00
OverrideMenu ( menu ) ;
RemoveMenu ( menu - > window . name ) ;
MenuList [ menu - > window . name ] = menu ;
2019-10-03 13:23:06 -04:00
2017-01-19 16:23:59 -05:00
return menu ;
}
2019-10-03 13:23:06 -04:00
Game : : MenuList * Menus : : LoadCustomMenuList ( const std : : string & menu , Utils : : Memory : : Allocator * allocator )
{
std : : vector < std : : pair < bool , Game : : menuDef_t * > > menus ;
FileSystem : : File menuFile ( menu ) ;
if ( ! menuFile . exists ( ) ) return nullptr ;
Game : : pc_token_t token ;
2022-12-14 06:31:03 -05:00
int handle = LoadMenuSource ( menu , menuFile . getBuffer ( ) ) ;
2019-10-03 13:23:06 -04:00
2022-12-14 06:31:03 -05:00
if ( IsValidSourceHandle ( handle ) )
2019-10-03 13:23:06 -04:00
{
while ( true )
{
ZeroMemory ( & token , sizeof ( token ) ) ;
if ( ! Game : : PC_ReadTokenHandle ( handle , & token ) | | token . string [ 0 ] = = ' } ' )
{
break ;
}
if ( ! _stricmp ( token . string , " loadmenu " ) )
{
Game : : PC_ReadTokenHandle ( handle , & token ) ;
2022-12-02 14:18:04 -05:00
auto * filename = Utils : : String : : VA ( " ui_mp \\ %s.menu " , token . string ) ;
2022-12-14 06:31:03 -05:00
Utils : : Merge ( & menus , LoadMenu ( filename ) ) ;
2019-10-03 13:23:06 -04:00
}
if ( ! _stricmp ( token . string , " menudef " ) )
{
2022-12-14 06:31:03 -05:00
auto * menudef = ParseMenu ( handle ) ;
if ( menudef ) menus . emplace_back ( std : : make_pair ( true , menudef ) ) ; // Custom menu
2019-10-03 13:23:06 -04:00
}
}
2022-12-14 06:31:03 -05:00
FreeMenuSource ( handle ) ;
2019-10-03 13:23:06 -04:00
}
if ( menus . empty ( ) ) return nullptr ;
// Allocate new menu list
2022-12-14 06:31:03 -05:00
auto * list = allocator - > allocate < Game : : MenuList > ( ) ;
2019-10-03 13:23:06 -04:00
if ( ! list ) return nullptr ;
list - > menus = allocator - > allocateArray < Game : : menuDef_t * > ( menus . size ( ) ) ;
if ( ! list - > menus )
{
allocator - > free ( list ) ;
return nullptr ;
}
list - > name = allocator - > duplicateString ( menu ) ;
2022-12-14 06:31:03 -05:00
list - > menuCount = static_cast < int > ( menus . size ( ) ) ;
2019-10-03 13:23:06 -04:00
// Copy new menus
2022-12-14 06:31:03 -05:00
for ( std : : size_t i = 0 ; i < menus . size ( ) ; + + i )
2019-10-03 13:23:06 -04:00
{
list - > menus [ i ] = menus [ i ] . second ;
}
return list ;
}
std : : vector < std : : pair < bool , Game : : menuDef_t * > > Menus : : LoadMenu ( const std : : string & menu )
2017-01-19 16:23:59 -05:00
{
2019-10-03 13:23:06 -04:00
std : : vector < std : : pair < bool , Game : : menuDef_t * > > menus ;
2017-01-19 16:23:59 -05:00
FileSystem : : File menuFile ( menu ) ;
if ( menuFile . exists ( ) )
{
Game : : pc_token_t token ;
2022-12-14 06:31:03 -05:00
const auto handle = LoadMenuSource ( menu , menuFile . getBuffer ( ) ) ;
2017-01-19 16:23:59 -05:00
2022-12-14 06:31:03 -05:00
if ( IsValidSourceHandle ( handle ) )
2017-01-19 16:23:59 -05:00
{
while ( true )
{
ZeroMemory ( & token , sizeof ( token ) ) ;
if ( ! Game : : PC_ReadTokenHandle ( handle , & token ) | | token . string [ 0 ] = = ' } ' )
{
break ;
}
if ( ! _stricmp ( token . string , " loadmenu " ) )
{
Game : : PC_ReadTokenHandle ( handle , & token ) ;
2022-12-14 06:31:03 -05:00
Utils : : Merge ( & menus , LoadMenu ( Utils : : String : : VA ( " ui_mp \\ %s.menu " , token . string ) ) ) ;
2017-01-19 16:23:59 -05:00
}
if ( ! _stricmp ( token . string , " menudef " ) )
{
2022-12-14 06:31:03 -05:00
auto * menudef = ParseMenu ( handle ) ;
if ( menudef ) menus . emplace_back ( std : : make_pair ( true , menudef ) ) ; // Custom menu
2017-01-19 16:23:59 -05:00
}
}
2022-12-14 06:31:03 -05:00
FreeMenuSource ( handle ) ;
2017-01-19 16:23:59 -05:00
}
}
2019-10-03 13:23:06 -04:00
return menus ;
}
std : : vector < std : : pair < bool , Game : : menuDef_t * > > Menus : : LoadMenu ( Game : : menuDef_t * menudef )
{
2022-12-14 06:31:03 -05:00
assert ( menudef - > window . name ) ;
std : : vector < std : : pair < bool , Game : : menuDef_t * > > menus = LoadMenu ( Utils : : String : : VA ( " ui_mp \\ %s.menu " , menudef - > window . name ) ) ;
2019-10-03 13:23:06 -04:00
if ( menus . empty ( ) )
{
2022-12-14 06:31:03 -05:00
menus . emplace_back ( std : : make_pair ( false , menudef ) ) ; // Native menu
2019-10-03 13:23:06 -04:00
}
2017-01-19 16:23:59 -05:00
2019-01-22 12:06:07 -05:00
return menus ;
2017-01-19 16:23:59 -05:00
}
2019-10-03 13:23:06 -04:00
Game : : MenuList * Menus : : LoadScriptMenu ( const char * menu )
{
Utils : : Memory : : Allocator * allocator = Utils : : Memory : : GetAllocator ( ) ;
2022-12-14 06:31:03 -05:00
auto menus = LoadMenu ( menu ) ;
2019-10-03 13:23:06 -04:00
if ( menus . empty ( ) ) return nullptr ;
// Allocate new menu list
2022-04-15 05:16:22 -04:00
auto * newList = allocator - > allocate < Game : : MenuList > ( ) ;
2019-10-03 13:23:06 -04:00
if ( ! newList ) return nullptr ;
newList - > menus = allocator - > allocateArray < Game : : menuDef_t * > ( menus . size ( ) ) ;
if ( ! newList - > menus )
{
allocator - > free ( newList ) ;
return nullptr ;
}
newList - > name = allocator - > duplicateString ( menu ) ;
2022-04-15 05:16:22 -04:00
newList - > menuCount = static_cast < int > ( menus . size ( ) ) ;
2019-10-03 13:23:06 -04:00
// Copy new menus
for ( unsigned int i = 0 ; i < menus . size ( ) ; + + i )
{
newList - > menus [ i ] = menus [ i ] . second ;
}
2022-12-14 06:31:03 -05:00
RemoveMenuList ( newList - > name ) ;
MenuListList [ newList - > name ] = newList ;
2019-10-03 13:23:06 -04:00
return newList ;
}
void Menus : : SafeMergeMenus ( std : : vector < std : : pair < bool , Game : : menuDef_t * > > * menus , std : : vector < std : : pair < bool , Game : : menuDef_t * > > newMenus )
{
// Check if we overwrote a menu
for ( auto i = menus - > begin ( ) ; i ! = menus - > end ( ) ; )
{
// Try to find the native menu
bool found = ! i - > first ; // Only if custom menu, try to find it
// If there is none, try to find a custom menu
if ( ! found )
{
for ( auto & entry : Menus : : MenuList )
{
if ( i - > second = = entry . second )
{
found = true ;
break ;
}
}
}
// Remove the menu if it has been deallocated (not found)
if ( ! found )
{
i = menus - > erase ( i ) ;
continue ;
}
bool increment = true ;
// Remove the menu if it has been loaded twice
for ( auto & newMenu : newMenus )
{
if ( i - > second - > window . name = = std : : string ( newMenu . second - > window . name ) )
{
2022-12-14 06:31:03 -05:00
RemoveMenu ( i - > second ) ;
2019-10-03 13:23:06 -04:00
i = menus - > erase ( i ) ;
increment = false ;
break ;
}
}
if ( increment ) + + i ;
}
Utils : : Merge ( menus , newMenus ) ;
}
Game : : MenuList * Menus : : LoadMenuList ( Game : : MenuList * menuList )
{
Utils : : Memory : : Allocator * allocator = Utils : : Memory : : GetAllocator ( ) ;
std : : vector < std : : pair < bool , Game : : menuDef_t * > > menus ;
for ( int i = 0 ; i < menuList - > menuCount ; + + i )
{
if ( ! menuList - > menus [ i ] ) continue ;
2022-12-14 06:31:03 -05:00
SafeMergeMenus ( & menus , LoadMenu ( menuList - > menus [ i ] ) ) ;
2019-10-03 13:23:06 -04:00
}
// Load custom menus
if ( menuList - > name = = " ui_mp/code.txt " s ) // Should be menus, but code is loaded ingame
{
2022-12-14 06:31:03 -05:00
for ( auto menu : CustomMenus )
2019-10-03 13:23:06 -04:00
{
bool hasMenu = false ;
for ( auto & loadedMenu : menus )
{
if ( loadedMenu . second - > window . name = = menu )
{
hasMenu = true ;
break ;
}
}
2022-12-14 06:31:03 -05:00
if ( ! hasMenu ) SafeMergeMenus ( & menus , LoadMenu ( menu ) ) ;
2019-10-03 13:23:06 -04:00
}
}
// Allocate new menu list
2022-12-14 06:31:03 -05:00
auto * newList = allocator - > allocate < Game : : MenuList > ( ) ;
2019-10-03 13:23:06 -04:00
if ( ! newList ) return menuList ;
2022-12-14 06:31:03 -05:00
auto size = menus . size ( ) ;
2019-10-03 13:23:06 -04:00
newList - > menus = allocator - > allocateArray < Game : : menuDef_t * > ( size ) ;
if ( ! newList - > menus )
{
allocator - > free ( newList ) ;
return menuList ;
}
newList - > name = allocator - > duplicateString ( menuList - > name ) ;
newList - > menuCount = size ;
// Copy new menus
for ( unsigned int i = 0 ; i < menus . size ( ) ; + + i )
{
newList - > menus [ i ] = menus [ i ] . second ;
}
2022-12-14 06:31:03 -05:00
RemoveMenuList ( newList - > name ) ;
MenuListList [ newList - > name ] = newList ;
2019-10-03 13:23:06 -04:00
return newList ;
}
2017-01-19 16:23:59 -05:00
void Menus : : FreeMenuSource ( int handle )
{
2017-06-02 09:36:20 -04:00
Utils : : Memory : : Allocator * allocator = Utils : : Memory : : GetAllocator ( ) ;
2017-04-16 05:47:57 -04:00
2022-12-14 06:31:03 -05:00
if ( ! IsValidSourceHandle ( handle ) ) return ;
2017-01-19 16:23:59 -05:00
2019-10-03 13:23:06 -04:00
Game : : source_t * source = Game : : sourceFiles [ handle ] ;
2017-01-19 16:23:59 -05:00
while ( source - > scriptstack )
{
Game : : script_t * script = source - > scriptstack ;
source - > scriptstack = source - > scriptstack - > next ;
Game : : FreeMemory ( script ) ;
}
while ( source - > tokens )
{
Game : : token_t * token = source - > tokens ;
source - > tokens = source - > tokens - > next ;
Game : : FreeMemory ( token ) ;
}
while ( source - > defines )
{
Game : : define_t * define = source - > defines ;
source - > defines = source - > defines - > next ;
Game : : FreeMemory ( define ) ;
}
while ( source - > indentstack )
{
Game : : indent_t * indent = source - > indentstack ;
source - > indentstack = source - > indentstack - > next ;
2017-04-16 05:47:57 -04:00
allocator - > free ( indent ) ;
2017-01-19 16:23:59 -05:00
}
2017-04-16 05:47:57 -04:00
if ( source - > definehash ) allocator - > free ( source - > definehash ) ;
2017-01-19 16:23:59 -05:00
2017-04-16 05:47:57 -04:00
allocator - > free ( source ) ;
2017-01-19 16:23:59 -05:00
Game : : sourceFiles [ handle ] = nullptr ;
}
2022-12-14 06:31:03 -05:00
void Menus : : Menu_FreeItemMemory ( Game : : itemDef_s * item )
2017-01-19 16:23:59 -05:00
{
2022-12-14 06:31:03 -05:00
AssertOffset ( Game : : itemDef_s , floatExpressionCount , 0x13C ) ;
2017-04-16 05:47:57 -04:00
2022-12-14 06:31:03 -05:00
for ( auto i = 0 ; i < item - > floatExpressionCount ; + + i )
2017-01-19 16:23:59 -05:00
{
2022-12-14 06:31:03 -05:00
Game : : free_expression ( item - > floatExpressions [ i ] . expression ) ;
}
item - > floatExpressionCount = 0 ;
}
void Menus : : FreeMenu ( Game : : menuDef_t * menu )
{
Utils : : Memory : : Allocator * allocator = Utils : : Memory : : GetAllocator ( ) ;
2019-10-03 13:23:06 -04:00
2022-12-14 06:31:03 -05:00
if ( menu - > items )
{
for ( int i = 0 ; i < menu - > itemCount ; + + i )
{
#if 0
Menu_FreeItemMemory ( menu - > items [ i ] ) ;
# endif
}
2019-10-03 13:23:06 -04:00
2022-12-14 06:31:03 -05:00
allocator - > free ( menu - > items ) ;
2017-01-19 16:23:59 -05:00
}
2022-12-14 06:31:03 -05:00
Game : : free_expression ( menu - > visibleExp ) ;
Game : : free_expression ( menu - > rectXExp ) ;
Game : : free_expression ( menu - > rectYExp ) ;
Game : : free_expression ( menu - > rectWExp ) ;
Game : : free_expression ( menu - > rectHExp ) ;
Game : : free_expression ( menu - > openSoundExp ) ;
Game : : free_expression ( menu - > closeSoundExp ) ;
allocator - > free ( menu ) ;
2017-01-19 16:23:59 -05:00
}
2019-10-03 13:23:06 -04:00
void Menus : : FreeMenuList ( Game : : MenuList * menuList )
{
if ( ! menuList ) return ;
Utils : : Memory : : Allocator * allocator = Utils : : Memory : : GetAllocator ( ) ;
// Keep our compiler happy
Game : : MenuList list = { menuList - > name , menuList - > menuCount , menuList - > menus } ;
2022-12-14 06:31:03 -05:00
allocator - > free ( list . name ) ;
allocator - > free ( list . menus ) ;
2019-10-03 13:23:06 -04:00
allocator - > free ( menuList ) ;
}
void Menus : : RemoveMenu ( const std : : string & menu )
{
2022-12-14 06:31:03 -05:00
auto i = MenuList . find ( menu ) ;
if ( i ! = MenuList . end ( ) )
2019-10-03 13:23:06 -04:00
{
2022-12-14 06:31:03 -05:00
if ( i - > second ) FreeMenu ( i - > second ) ;
i = MenuList . erase ( i ) ;
2019-10-03 13:23:06 -04:00
}
}
void Menus : : RemoveMenu ( Game : : menuDef_t * menudef )
{
2022-12-14 06:31:03 -05:00
for ( auto i = MenuList . begin ( ) ; i ! = MenuList . end ( ) ; )
2019-10-03 13:23:06 -04:00
{
if ( i - > second = = menudef )
{
2022-12-14 06:31:03 -05:00
FreeMenu ( menudef ) ;
i = MenuList . erase ( i ) ;
2019-10-03 13:23:06 -04:00
}
else
{
+ + i ;
}
}
}
void Menus : : RemoveMenuList ( const std : : string & menuList )
{
2022-12-14 06:31:03 -05:00
auto i = MenuListList . find ( menuList ) ;
if ( i ! = MenuListList . end ( ) )
2019-10-03 13:23:06 -04:00
{
if ( i - > second )
{
for ( auto j = 0 ; j < i - > second - > menuCount ; + + j )
{
2022-12-14 06:31:03 -05:00
RemoveMenu ( i - > second - > menus [ j ] ) ;
2019-10-03 13:23:06 -04:00
}
2022-12-14 06:31:03 -05:00
FreeMenuList ( i - > second ) ;
2019-10-03 13:23:06 -04:00
}
2022-12-14 06:31:03 -05:00
i = MenuListList . erase ( i ) ;
2019-10-03 13:23:06 -04:00
}
}
// This is actually a really important function
// It checks if we have already loaded the menu we passed and replaces its instances in memory
// Due to deallocating the old menu, the game might crash on not being able to handle its old instance
// So we need to override it in our menu lists and the game's ui context
// EDIT: We might also remove the old instances inside RemoveMenu
// EDIT2: Removing old instances without having a menu to replace them with might leave a nullptr
// EDIT3: Wouldn't it be better to check if the new menu we're trying to load has already been loaded and not was not deallocated and return that one instead of loading a new one?
void Menus : : OverrideMenu ( Game : : menuDef_t * menu )
{
if ( ! menu | | ! menu - > window . name ) return ;
std : : string name = menu - > window . name ;
// Find the old menu
2022-12-14 06:31:03 -05:00
if ( auto i = MenuList . find ( name ) ; i ! = MenuList . end ( ) )
2019-10-03 13:23:06 -04:00
{
// We have found it, *yay*
Game : : menuDef_t * oldMenu = i - > second ;
// Replace every old instance with our new one in the ui context
for ( int j = 0 ; j < Game : : uiContext - > menuCount ; + + j )
{
if ( Game : : uiContext - > Menus [ j ] = = oldMenu )
{
Game : : uiContext - > Menus [ j ] = menu ;
}
}
// Replace every old instance with our new one in our menu lists
2022-12-14 06:31:03 -05:00
for ( auto j = MenuListList . begin ( ) ; j ! = MenuListList . end ( ) ; + + j )
2019-10-03 13:23:06 -04:00
{
Game : : MenuList * list = j - > second ;
if ( list & & list - > menus )
{
for ( int k = 0 ; k < list - > menuCount ; + + k )
{
if ( list - > menus [ k ] = = oldMenu )
{
list - > menus [ k ] = menu ;
}
}
}
}
2022-12-14 06:31:03 -05:00
FreeMenu ( oldMenu ) ;
2019-10-03 13:23:06 -04:00
}
}
void Menus : : RemoveMenuList ( Game : : MenuList * menuList )
{
if ( ! menuList | | ! menuList - > name ) return ;
2022-12-14 06:31:03 -05:00
RemoveMenuList ( menuList - > name ) ;
2019-10-03 13:23:06 -04:00
}
2022-12-14 06:31:03 -05:00
// In your dreams
2017-01-19 16:23:59 -05:00
void Menus : : FreeEverything ( )
{
2022-12-14 06:31:03 -05:00
for ( auto i = MenuListList . begin ( ) ; i ! = MenuListList . end ( ) ; + + i )
2019-10-03 13:23:06 -04:00
{
2022-12-14 06:31:03 -05:00
FreeMenuList ( i - > second ) ;
2019-10-03 13:23:06 -04:00
}
2022-12-14 06:31:03 -05:00
MenuListList . clear ( ) ;
2019-10-03 13:23:06 -04:00
2022-12-14 06:31:03 -05:00
for ( auto i = MenuList . begin ( ) ; i ! = MenuList . end ( ) ; + + i )
2019-10-03 13:23:06 -04:00
{
2022-12-14 06:31:03 -05:00
FreeMenu ( i - > second ) ;
2019-10-03 13:23:06 -04:00
}
2022-12-14 06:31:03 -05:00
MenuList . clear ( ) ;
2019-10-03 13:23:06 -04:00
}
Game : : XAssetHeader Menus : : MenuFindHook ( Game : : XAssetType /*type*/ , const std : : string & filename )
{
return { Game : : Menus_FindByName ( Game : : uiContext , filename . data ( ) ) } ;
}
Game : : XAssetHeader Menus : : MenuListFindHook ( Game : : XAssetType type , const std : : string & filename )
{
Game : : XAssetHeader header = { nullptr } ;
// Free the last menulist and ui context, as we have to rebuild it with the new menus
2022-12-14 06:31:03 -05:00
if ( MenuListList . find ( filename ) ! = MenuListList . end ( ) )
2019-10-03 13:23:06 -04:00
{
2022-12-14 06:31:03 -05:00
Game : : MenuList * list = MenuListList [ filename ] ;
2019-10-03 13:23:06 -04:00
for ( int i = 0 ; list & & list - > menus & & i < list - > menuCount ; + + i )
{
2022-12-14 06:31:03 -05:00
RemoveMenuFromContext ( Game : : uiContext , list - > menus [ i ] ) ;
2019-10-03 13:23:06 -04:00
}
2022-12-14 06:31:03 -05:00
RemoveMenuList ( filename ) ;
2019-10-03 13:23:06 -04:00
}
if ( Utils : : String : : EndsWith ( filename , " .menu " ) )
{
if ( FileSystem : : File ( filename ) . exists ( ) )
{
2022-12-14 06:31:03 -05:00
header . menuList = LoadScriptMenu ( filename . data ( ) ) ;
2019-10-03 13:23:06 -04:00
if ( header . menuList ) return header ;
}
}
Game : : MenuList * menuList = Game : : DB_FindXAssetHeader ( type , filename . data ( ) ) . menuList ;
header . menuList = menuList ;
if ( menuList & & reinterpret_cast < DWORD > ( menuList ) ! = 0xDDDDDDDD )
2017-01-19 16:23:59 -05:00
{
2019-10-03 13:23:06 -04:00
// Parse scriptmenus!
if ( ( menuList - > menuCount > 0 & & menuList - > menus [ 0 ] & & menuList - > menus [ 0 ] - > window . name = = " default_menu " s ) )
{
if ( FileSystem : : File ( filename ) . exists ( ) )
{
2022-12-14 06:31:03 -05:00
header . menuList = LoadScriptMenu ( filename . data ( ) ) ;
2019-10-03 13:23:06 -04:00
// Reset, if we didn't find scriptmenus
if ( ! header . menuList )
{
header . menuList = menuList ;
}
}
}
else
{
2022-12-14 06:31:03 -05:00
header . menuList = LoadMenuList ( menuList ) ;
2019-10-03 13:23:06 -04:00
}
}
else
{
header . menuList = nullptr ;
2017-01-19 16:23:59 -05:00
}
2019-10-03 13:23:06 -04:00
return header ;
2017-01-19 16:23:59 -05:00
}
2019-10-03 13:23:06 -04:00
bool Menus : : IsMenuVisible ( Game : : UiContext * dc , Game : : menuDef_t * menu )
2017-01-19 16:23:59 -05:00
{
if ( menu & & menu - > window . name )
{
if ( menu - > window . name = = " connect " s ) // Check if we're supposed to draw the loadscreen
{
Game : : menuDef_t * originalConnect = AssetHandler : : FindOriginalAsset ( Game : : XAssetType : : ASSET_TYPE_MENU , " connect " ) . menu ;
if ( originalConnect = = menu ) // Check if we draw the original loadscreen
{
2022-12-14 06:31:03 -05:00
if ( MenuList . find ( " connect " ) ! = Menus : : MenuList . end ( ) ) // Check if we have a custom loadscreen, to prevent drawing the original one on top
2017-01-19 16:23:59 -05:00
{
return false ;
}
}
}
}
return Game : : Menu_IsVisible ( dc , menu ) ;
}
2019-10-03 13:23:06 -04:00
void Menus : : RemoveMenuFromContext ( Game : : UiContext * dc , Game : : menuDef_t * menu )
{
// Search menu in context
int i = 0 ;
for ( ; i < dc - > menuCount ; + + i )
{
if ( dc - > Menus [ i ] = = menu )
{
break ;
}
}
// Remove from stack
if ( i < dc - > menuCount )
{
for ( ; i < dc - > menuCount - 1 ; + + i )
{
dc - > Menus [ i ] = dc - > Menus [ i + 1 ] ;
}
// Clear last menu
dc - > Menus [ - - dc - > menuCount ] = nullptr ;
}
2022-12-14 06:31:03 -05:00
FreeMenu ( menu ) ;
2019-10-03 13:23:06 -04:00
}
void Menus : : Add ( const std : : string & menu )
{
2022-12-14 06:31:03 -05:00
CustomMenus . push_back ( menu ) ;
2019-10-03 13:23:06 -04:00
}
2017-01-19 16:23:59 -05:00
Menus : : Menus ( )
{
2022-12-14 06:31:03 -05:00
menuParseKeywordHash = reinterpret_cast < Game : : KeywordHashEntry < Game : : menuDef_t , 128 , 3523 > * * > ( 0x63AE928 ) ;
2022-11-30 19:00:18 -05:00
if ( ZoneBuilder : : IsEnabled ( ) )
{
Game : : Menu_Setup ( Game : : uiContext ) ;
}
2017-01-19 16:23:59 -05:00
if ( Dedicated : : IsEnabled ( ) ) return ;
2019-10-03 13:23:06 -04:00
// Intercept asset finding
2022-12-14 06:31:03 -05:00
AssetHandler : : OnFind ( Game : : XAssetType : : ASSET_TYPE_MENU , MenuFindHook ) ;
AssetHandler : : OnFind ( Game : : XAssetType : : ASSET_TYPE_MENULIST , MenuListFindHook ) ;
2019-10-03 13:23:06 -04:00
2017-01-19 16:23:59 -05:00
// Don't open connect menu
2022-12-14 06:31:03 -05:00
// Utils::Hook::Nop(0x428E48, 5);
2017-01-19 16:23:59 -05:00
// Use the connect menu open call to update server motds
2022-12-14 06:31:03 -05:00
Utils : : Hook ( 0x428E48 , [ ]
2022-04-15 05:16:22 -04:00
{
if ( ! Party : : GetMotd ( ) . empty ( ) & & Party : : Target ( ) = = * Game : : connectedHost )
2017-01-19 16:23:59 -05:00
{
2022-04-15 05:16:22 -04:00
Dvar : : Var ( " didyouknow " ) . set ( Party : : GetMotd ( ) ) ;
}
} , HOOK_CALL ) . install ( ) - > quick ( ) ;
2017-01-19 16:23:59 -05:00
2022-08-24 10:38:14 -04:00
// Intercept menu painting
2022-12-14 06:31:03 -05:00
Utils : : Hook ( 0x4FFBDF , IsMenuVisible , HOOK_CALL ) . install ( ) - > quick ( ) ;
2017-01-19 16:23:59 -05:00
2022-12-14 06:31:03 -05:00
// disable the 2 new tokens in ItemParse_rect (Fix by NTA. Probably because he didn't want to update the menus)
Utils : : Hook : : Set < std : : uint8_t > ( 0x640693 , 0xEB ) ;
2017-01-19 16:23:59 -05:00
2022-08-24 10:38:14 -04:00
// don't load ASSET_TYPE_MENU assets for every menu (might cause patch menus to fail)
Utils : : Hook : : Nop ( 0x453406 , 5 ) ;
2017-01-19 16:23:59 -05:00
2022-08-24 10:38:14 -04:00
// make Com_Error and similar go back to main_text instead of menu_xboxlive.
Utils : : Hook : : SetString ( 0x6FC790 , " main_text " ) ;
2017-01-19 16:23:59 -05:00
2022-08-24 10:38:14 -04:00
Command : : Add ( " openmenu " , [ ] ( Command : : Params * params )
{
if ( params - > size ( ) ! = 2 )
2022-04-15 05:16:22 -04:00
{
2022-08-24 10:38:14 -04:00
Logger : : Print ( " USAGE: openmenu <menu name> \n " ) ;
return ;
}
2017-01-19 16:23:59 -05:00
2022-08-24 10:38:14 -04:00
// Not quite sure if we want to do this if we're not ingame, but it's only needed for ingame menus.
2022-08-24 17:46:07 -04:00
if ( ( * Game : : cl_ingame ) - > current . enabled )
2022-04-15 05:16:22 -04:00
{
2022-12-14 06:31:03 -05:00
Game : : Key_SetCatcher ( 0 , Game : : KEYCATCH_UI ) ;
2022-08-24 10:38:14 -04:00
}
2017-01-19 16:23:59 -05:00
2022-08-24 10:38:14 -04:00
Game : : Menus_OpenByName ( Game : : uiContext , params - > get ( 1 ) ) ;
} ) ;
2017-01-19 16:23:59 -05:00
2022-12-14 06:31:03 -05:00
Command : : Add ( " reloadmenus " , [ ] ( [[maybe_unused]] Command : : Params * params )
2022-08-24 10:38:14 -04:00
{
// Close all menus
Game : : Menus_CloseAll ( Game : : uiContext ) ;
2017-01-19 16:23:59 -05:00
2022-12-14 06:31:03 -05:00
// Free custom menus (Get pranked)
FreeEverything ( ) ;
2017-06-11 15:24:32 -04:00
2022-08-24 10:38:14 -04:00
// Only disconnect if in-game, context is updated automatically!
if ( Game : : CL_IsCgameInitialized ( ) )
2022-04-15 05:16:22 -04:00
{
2022-08-24 10:38:14 -04:00
Game : : Cbuf_AddText ( 0 , " disconnect \n " ) ;
}
else
{
// Reinitialize ui context
Utils : : Hook : : Call < void ( ) > ( 0x401700 ) ( ) ;
// Reopen main menu
Game : : Menus_OpenByName ( Game : : uiContext , " main_text " ) ;
}
} ) ;
Command : : Add ( " mp_QuickMessage " , [ ] ( Command : : Params * )
{
Command : : Execute ( " openmenu quickmessage " ) ;
} ) ;
// Define custom menus here
2022-12-14 06:31:03 -05:00
Add ( " ui_mp/changelog.menu " ) ;
Add ( " ui_mp/theater_menu.menu " ) ;
Add ( " ui_mp/pc_options_multi.menu " ) ;
Add ( " ui_mp/pc_options_game.menu " ) ;
Add ( " ui_mp/pc_options_gamepad.menu " ) ;
Add ( " ui_mp/stats_reset.menu " ) ;
Add ( " ui_mp/stats_unlock.menu " ) ;
Add ( " ui_mp/security_increase_popmenu.menu " ) ;
Add ( " ui_mp/mod_download_popmenu.menu " ) ;
Add ( " ui_mp/popup_friends.menu " ) ;
Add ( " ui_mp/menu_first_launch.menu " ) ;
Add ( " ui_mp/startup_messages.menu " ) ;
Add ( " ui_mp/iw4x_credits.menu " ) ;
Add ( " ui_mp/resetclass.menu " ) ;
Add ( " ui_mp/popup_customtitle.menu " ) ;
Add ( " ui_mp/popup_customclan.menu " ) ;
2017-01-19 16:23:59 -05:00
}
Menus : : ~ Menus ( )
{
2022-12-14 06:31:03 -05:00
// Let Windows handle the memory leaks for you!
2017-01-19 16:23:59 -05:00
Menus : : FreeEverything ( ) ;
}
}