mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
698 lines
22 KiB
C
698 lines
22 KiB
C
/*
|
|
gameinfo.c - gameinfo.txt / liblist.gam parsing for filesystem
|
|
Copyright (C) 2003-2006 Mathieu Olivier
|
|
Copyright (C) 2000-2007 DarkPlaces contributors
|
|
Copyright (C) 2007 Uncle Mike
|
|
Copyright (C) 2015-2023 Xash3D FWGS contributors
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
*/
|
|
|
|
#include "build.h"
|
|
#include <time.h>
|
|
#include <stdio.h>
|
|
#include "defaults.h"
|
|
#include "crtlib.h"
|
|
#include "filesystem.h"
|
|
#include "filesystem_internal.h"
|
|
#include "xash3d_mathlib.h"
|
|
#include "common/com_strings.h"
|
|
#include "common/protocol.h"
|
|
#include "library_suffix.h"
|
|
|
|
#define SAVE_AGED_COUNT 2 // the default count of quick and auto saves
|
|
|
|
/*
|
|
================
|
|
FS_WriteGameInfo
|
|
|
|
assume GameInfo is valid
|
|
================
|
|
*/
|
|
static qboolean FS_WriteGameInfo( const char *filepath, const gameinfo_t *GameInfo )
|
|
{
|
|
file_t *f = FS_Open( filepath, "w", false ); // we in binary-mode
|
|
int write_ambients = false;
|
|
|
|
if( !f )
|
|
return false;
|
|
|
|
FS_Printf( f, "// generated by " XASH_ENGINE_NAME " " XASH_VERSION "-%s (%s-%s)\n\n\n", g_buildcommit, Q_buildos(), Q_buildarch() );
|
|
|
|
if( !COM_StringEmpty( GameInfo->basedir ))
|
|
FS_Printf( f, "basedir\t\t\"%s\"\n", GameInfo->basedir );
|
|
|
|
// DEPRECATED: gamedir key isn't supported by FWGS fork
|
|
// but write it anyway to keep compability with original Xash3D
|
|
if( !COM_StringEmpty( GameInfo->gamefolder ))
|
|
FS_Printf( f, "gamedir\t\t\"%s\"\n", GameInfo->gamefolder );
|
|
|
|
if( !COM_StringEmpty( GameInfo->falldir ))
|
|
FS_Printf( f, "fallback_dir\t\"%s\"\n", GameInfo->falldir );
|
|
|
|
if( !COM_StringEmpty( GameInfo->title ))
|
|
FS_Printf( f, "title\t\t\"%s\"\n", GameInfo->title );
|
|
|
|
if( !COM_StringEmpty( GameInfo->startmap ))
|
|
FS_Printf( f, "startmap\t\t\"%s\"\n", GameInfo->startmap );
|
|
|
|
if( !COM_StringEmpty( GameInfo->trainmap ))
|
|
FS_Printf( f, "trainmap\t\t\"%s\"\n", GameInfo->trainmap );
|
|
|
|
if( GameInfo->version != 0.0f )
|
|
FS_Printf( f, "version\t\t%g\n", GameInfo->version );
|
|
|
|
if( GameInfo->size != 0 )
|
|
FS_Printf( f, "size\t\t%zu\n", GameInfo->size );
|
|
|
|
if( !COM_StringEmpty( GameInfo->game_url ))
|
|
FS_Printf( f, "url_info\t\t\"%s\"\n", GameInfo->game_url );
|
|
|
|
if( !COM_StringEmpty( GameInfo->update_url ))
|
|
FS_Printf( f, "url_update\t\t\"%s\"\n", GameInfo->update_url );
|
|
|
|
if( !COM_StringEmpty( GameInfo->type ))
|
|
FS_Printf( f, "type\t\t\"%s\"\n", GameInfo->type );
|
|
|
|
if( !COM_StringEmpty( GameInfo->date ))
|
|
FS_Printf( f, "date\t\t\"%s\"\n", GameInfo->date );
|
|
|
|
if( !COM_StringEmpty( GameInfo->dll_path ))
|
|
FS_Printf( f, "dllpath\t\t\"%s\"\n", GameInfo->dll_path );
|
|
|
|
if( !COM_StringEmpty( GameInfo->game_dll ))
|
|
FS_Printf( f, "gamedll\t\t\"%s\"\n", GameInfo->game_dll );
|
|
|
|
if( !COM_StringEmpty( GameInfo->game_dll_linux ))
|
|
FS_Printf( f, "gamedll_linux\t\t\"%s\"\n", GameInfo->game_dll_linux );
|
|
|
|
if( !COM_StringEmpty( GameInfo->game_dll_osx ))
|
|
FS_Printf( f, "gamedll_osx\t\t\"%s\"\n", GameInfo->game_dll_osx );
|
|
|
|
if( !COM_StringEmpty( GameInfo->iconpath ))
|
|
FS_Printf( f, "icon\t\t\"%s\"\n", GameInfo->iconpath );
|
|
|
|
switch( GameInfo->gamemode )
|
|
{
|
|
case 1: FS_Print( f, "gamemode\t\t\"singleplayer_only\"\n" ); break;
|
|
case 2: FS_Print( f, "gamemode\t\t\"multiplayer_only\"\n" ); break;
|
|
}
|
|
|
|
if( !COM_StringEmpty( GameInfo->sp_entity ))
|
|
FS_Printf( f, "sp_entity\t\t\"%s\"\n", GameInfo->sp_entity );
|
|
if( !COM_StringEmpty( GameInfo->mp_entity ))
|
|
FS_Printf( f, "mp_entity\t\t\"%s\"\n", GameInfo->mp_entity );
|
|
if( !COM_StringEmpty( GameInfo->mp_filter ))
|
|
FS_Printf( f, "mp_filter\t\t\"%s\"\n", GameInfo->mp_filter );
|
|
|
|
if( GameInfo->secure )
|
|
FS_Printf( f, "secure\t\t\"%i\"\n", GameInfo->secure );
|
|
|
|
if( GameInfo->nomodels )
|
|
FS_Printf( f, "nomodels\t\t\"%i\"\n", GameInfo->nomodels );
|
|
|
|
if( GameInfo->max_edicts > 0 )
|
|
FS_Printf( f, "max_edicts\t%i\n", GameInfo->max_edicts );
|
|
if( GameInfo->max_tents > 0 )
|
|
FS_Printf( f, "max_tempents\t%i\n", GameInfo->max_tents );
|
|
if( GameInfo->max_beams > 0 )
|
|
FS_Printf( f, "max_beams\t\t%i\n", GameInfo->max_beams );
|
|
if( GameInfo->max_particles > 0 )
|
|
FS_Printf( f, "max_particles\t%i\n", GameInfo->max_particles );
|
|
|
|
for( int i = 0; i < NUM_AMBIENTS; i++ )
|
|
{
|
|
if( *GameInfo->ambientsound[i] )
|
|
{
|
|
if( !write_ambients )
|
|
{
|
|
FS_Print( f, "\n" );
|
|
write_ambients = true;
|
|
}
|
|
FS_Printf( f, "ambient%i\t\t%s\n", i, GameInfo->ambientsound[i] );
|
|
}
|
|
}
|
|
|
|
if( GameInfo->noskills )
|
|
FS_Printf( f, "noskills\t\t\"%i\"\n", GameInfo->noskills );
|
|
|
|
if( GameInfo->quicksave_aged_count != SAVE_AGED_COUNT )
|
|
FS_Printf( f, "quicksave_aged_count\t\t%d\n", GameInfo->quicksave_aged_count );
|
|
|
|
if( GameInfo->autosave_aged_count != SAVE_AGED_COUNT )
|
|
FS_Printf( f, "autosave_aged_count\t\t%d\n", GameInfo->autosave_aged_count );
|
|
|
|
// HL25 compatibility
|
|
if( GameInfo->animated_title )
|
|
FS_Printf( f, "animated_title\t\t%i\n", GameInfo->animated_title );
|
|
|
|
if( GameInfo->hd_background )
|
|
FS_Printf( f, "hd_background\t\t%i\n", GameInfo->hd_background );
|
|
|
|
// always expose our extensions :)
|
|
FS_Printf( f, "internal_vgui_support\t\t%i\n", GameInfo->internal_vgui_support );
|
|
FS_Printf( f, "render_picbutton_text\t\t%i\n", GameInfo->render_picbutton_text );
|
|
|
|
if( !COM_StringEmpty( GameInfo->demomap ))
|
|
FS_Printf( f, "demomap\t\t\"%s\"\n", GameInfo->demomap );
|
|
|
|
FS_Close( f ); // all done
|
|
|
|
return true;
|
|
}
|
|
|
|
void FS_MakeGameInfo( void )
|
|
{
|
|
if( FS_WriteGameInfo( "gameinfo.txt", FI.GameInfo ))
|
|
Con_Printf( "Successfully generated %s/gameinfo.txt\n", FI.GameInfo->gamefolder );
|
|
else
|
|
Con_Printf( S_ERROR "Can't open %s/gameinfo.txt for write\n", FI.GameInfo->gamefolder );
|
|
}
|
|
|
|
static void FS_InitGameInfo( gameinfo_t *GameInfo, const char *gamedir, qboolean quake, time_t mtime )
|
|
{
|
|
memset( GameInfo, 0, sizeof( *GameInfo ));
|
|
|
|
// filesystem info
|
|
GameInfo->mtime = mtime;
|
|
Q_strncpy( GameInfo->gamefolder, gamedir, sizeof( GameInfo->gamefolder ));
|
|
Q_strncpy( GameInfo->sp_entity, "info_player_start", sizeof( GameInfo->sp_entity ));
|
|
Q_strncpy( GameInfo->mp_entity, "info_player_deathmatch", sizeof( GameInfo->mp_entity ));
|
|
Q_strncpy( GameInfo->iconpath, "game.ico", sizeof( GameInfo->iconpath ));
|
|
|
|
if( quake )
|
|
{
|
|
Q_strncpy( GameInfo->basedir, "id1", sizeof( GameInfo->basedir ));
|
|
Q_strncpy( GameInfo->falldir, "qwrap", sizeof( GameInfo->falldir ));
|
|
Q_strncpy( GameInfo->title, gamedir, sizeof( GameInfo->title ));
|
|
Q_strncpy( GameInfo->startmap, "start", sizeof( GameInfo->startmap ));
|
|
Q_strncpy( GameInfo->dll_path, "bin", sizeof( GameInfo->dll_path ));
|
|
Q_strncpy( GameInfo->game_dll, "bin/progs.dll", sizeof( GameInfo->game_dll ));
|
|
Q_strncpy( GameInfo->game_dll_linux, "bin/progs.so", sizeof( GameInfo->game_dll_linux ));
|
|
Q_strncpy( GameInfo->game_dll_osx, "bin/progs.dylib", sizeof( GameInfo->game_dll_osx ));
|
|
}
|
|
else
|
|
{
|
|
Q_strncpy( GameInfo->basedir, fs_basedir, sizeof( GameInfo->basedir ));
|
|
Q_strncpy( GameInfo->title, gamedir, sizeof( GameInfo->title ));
|
|
Q_strncpy( GameInfo->startmap, "c0a0", sizeof( GameInfo->startmap ));
|
|
Q_strncpy( GameInfo->dll_path, "cl_dlls", sizeof( GameInfo->dll_path ));
|
|
Q_strncpy( GameInfo->game_dll, "dlls/hl.dll", sizeof( GameInfo->game_dll ));
|
|
Q_strncpy( GameInfo->game_dll_linux, "dlls/hl.so", sizeof( GameInfo->game_dll_linux ));
|
|
Q_strncpy( GameInfo->game_dll_osx, "dlls/hl.dylib", sizeof( GameInfo->game_dll_osx ));
|
|
}
|
|
|
|
GameInfo->max_edicts = DEFAULT_MAX_EDICTS; // default value if not specified
|
|
GameInfo->max_tents = 500;
|
|
GameInfo->max_beams = 128;
|
|
GameInfo->max_particles = 4096;
|
|
GameInfo->version = 1.0f;
|
|
|
|
GameInfo->quicksave_aged_count = SAVE_AGED_COUNT;
|
|
GameInfo->autosave_aged_count = SAVE_AGED_COUNT;
|
|
}
|
|
|
|
static void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qboolean isGameInfo )
|
|
{
|
|
char *pfile = (char*) buf;
|
|
qboolean found_linux = false, found_osx = false;
|
|
string token;
|
|
|
|
while(( pfile = COM_ParseFile( pfile, token, sizeof( token ))) != NULL )
|
|
{
|
|
// different names in liblist/gameinfo
|
|
if( !Q_stricmp( token, isGameInfo ? "title" : "game" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->title, sizeof( GameInfo->title ));
|
|
}
|
|
// valid for both
|
|
else if( !Q_stricmp( token, "fallback_dir" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->falldir, sizeof( GameInfo->falldir ));
|
|
}
|
|
// valid for both
|
|
else if( !Q_stricmp( token, "startmap" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->startmap, sizeof( GameInfo->startmap ));
|
|
COM_StripExtension( GameInfo->startmap ); // HQ2:Amen has extension .bsp
|
|
}
|
|
// only trainmap is valid for gameinfo
|
|
else if( !Q_stricmp( token, "trainmap" ) ||
|
|
(!isGameInfo && !Q_stricmp( token, "trainingmap" )))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->trainmap, sizeof( GameInfo->trainmap ));
|
|
COM_StripExtension( GameInfo->trainmap ); // HQ2:Amen has extension .bsp
|
|
}
|
|
// valid for both
|
|
else if( !Q_stricmp( token, "url_info" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->game_url, sizeof( GameInfo->game_url ));
|
|
}
|
|
// different names
|
|
else if( !Q_stricmp( token, isGameInfo ? "url_update" : "url_dl" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->update_url, sizeof( GameInfo->update_url ));
|
|
}
|
|
// valid for both
|
|
else if( !Q_stricmp( token, "gamedll" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->game_dll, sizeof( GameInfo->game_dll ));
|
|
COM_FixSlashes( GameInfo->game_dll );
|
|
}
|
|
// valid for both
|
|
else if( !Q_stricmp( token, "gamedll_linux" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->game_dll_linux, sizeof( GameInfo->game_dll_linux ));
|
|
found_linux = true;
|
|
}
|
|
// valid for both
|
|
else if( !Q_stricmp( token, "gamedll_osx" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->game_dll_osx, sizeof( GameInfo->game_dll_osx ));
|
|
found_osx = true;
|
|
}
|
|
// valid for both
|
|
else if( !Q_stricmp( token, "icon" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->iconpath, sizeof( GameInfo->iconpath ));
|
|
COM_FixSlashes( GameInfo->iconpath );
|
|
COM_DefaultExtension( GameInfo->iconpath, ".ico", sizeof( GameInfo->iconpath ));
|
|
}
|
|
else if( !Q_stricmp( token, "type" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
|
|
if( isGameInfo )
|
|
{
|
|
Q_strncpy( GameInfo->type, token, sizeof( GameInfo->type ));
|
|
}
|
|
else
|
|
{
|
|
if( !Q_stricmp( token, "singleplayer_only" ))
|
|
{
|
|
// TODO: Remove this ugly hack too.
|
|
// This was made because Half-Life has multiplayer,
|
|
// but for some reason it's marked as singleplayer_only.
|
|
// Old WON version is fine.
|
|
if( !Q_stricmp( GameInfo->gamefolder, "valve") )
|
|
GameInfo->gamemode = GAME_NORMAL;
|
|
else
|
|
GameInfo->gamemode = GAME_SINGLEPLAYER_ONLY;
|
|
Q_strncpy( GameInfo->type, "Single", sizeof( GameInfo->type ));
|
|
}
|
|
else if( !Q_stricmp( token, "multiplayer_only" ))
|
|
{
|
|
GameInfo->gamemode = GAME_MULTIPLAYER_ONLY;
|
|
Q_strncpy( GameInfo->type, "Multiplayer", sizeof( GameInfo->type ));
|
|
}
|
|
else
|
|
{
|
|
// pass type without changes
|
|
if( !isGameInfo )
|
|
GameInfo->gamemode = GAME_NORMAL;
|
|
Q_strncpy( GameInfo->type, token, sizeof( GameInfo->type ));
|
|
}
|
|
}
|
|
}
|
|
// valid for both
|
|
else if( !Q_stricmp( token, "version" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->version = Q_atof( token );
|
|
}
|
|
// valid for both
|
|
else if( !Q_stricmp( token, "size" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->size = Q_atoi( token );
|
|
}
|
|
else if( !Q_stricmp( token, isGameInfo ? "mp_entity" : "mpentity" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->mp_entity, sizeof( GameInfo->mp_entity ));
|
|
}
|
|
else if( !Q_stricmp( token, isGameInfo ? "mp_filter" : "mpfilter" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->mp_filter, sizeof( GameInfo->mp_filter ));
|
|
}
|
|
// valid for both
|
|
else if( !Q_stricmp( token, "secure" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->secure = Q_atoi( token ) ? true : false;
|
|
}
|
|
// valid for both
|
|
else if( !Q_stricmp( token, "nomodels" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->nomodels = Q_atoi( token ) ? true : false;
|
|
}
|
|
else if( !Q_stricmp( token, isGameInfo ? "max_edicts" : "edicts" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->max_edicts = bound( MIN_EDICTS, Q_atoi( token ), MAX_EDICTS );
|
|
}
|
|
// valid for both
|
|
else if( !Q_stricmp( token, "hd_background" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->hd_background = Q_atoi( token ) ? true : false;
|
|
}
|
|
else if( !Q_stricmp( token, "animated_title" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->animated_title = Q_atoi( token ) ? true : false;
|
|
}
|
|
// only for gameinfo
|
|
else if( isGameInfo )
|
|
{
|
|
if( !Q_stricmp( token, "basedir" ))
|
|
{
|
|
string fs_path;
|
|
pfile = COM_ParseFile( pfile, fs_path, sizeof( fs_path ));
|
|
if( Q_stricmp( fs_path, GameInfo->basedir ) || Q_stricmp( fs_path, GameInfo->gamefolder ))
|
|
Q_strncpy( GameInfo->basedir, fs_path, sizeof( GameInfo->basedir ));
|
|
}
|
|
else if( !Q_stricmp( token, "sp_entity" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->sp_entity, sizeof( GameInfo->sp_entity ));
|
|
}
|
|
else if( isGameInfo && !Q_stricmp( token, "dllpath" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->dll_path, sizeof( GameInfo->dll_path ));
|
|
}
|
|
else if( isGameInfo && !Q_stricmp( token, "date" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->date, sizeof( GameInfo->date ));
|
|
}
|
|
else if( !Q_stricmp( token, "max_tempents" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->max_tents = bound( 300, Q_atoi( token ), 2048 );
|
|
}
|
|
else if( !Q_stricmp( token, "max_beams" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->max_beams = bound( 64, Q_atoi( token ), 512 );
|
|
}
|
|
else if( !Q_stricmp( token, "max_particles" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->max_particles = bound( 1024, Q_atoi( token ), 131072 );
|
|
}
|
|
else if( !Q_stricmp( token, "gamemode" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
// TODO: Remove this ugly hack too.
|
|
// This was made because Half-Life has multiplayer,
|
|
// but for some reason it's marked as singleplayer_only.
|
|
// Old WON version is fine.
|
|
if( !Q_stricmp( token, "singleplayer_only" ) && Q_stricmp( GameInfo->gamefolder, "valve") )
|
|
GameInfo->gamemode = GAME_SINGLEPLAYER_ONLY;
|
|
else if( !Q_stricmp( token, "multiplayer_only" ))
|
|
GameInfo->gamemode = GAME_MULTIPLAYER_ONLY;
|
|
}
|
|
else if( !Q_strnicmp( token, "ambient", 7 ))
|
|
{
|
|
int ambientNum = Q_atoi( token + 7 );
|
|
|
|
if( ambientNum < 0 || ambientNum >= NUM_AMBIENTS )
|
|
ambientNum = 0;
|
|
pfile = COM_ParseFile( pfile, GameInfo->ambientsound[ambientNum],
|
|
sizeof( GameInfo->ambientsound[ambientNum] ));
|
|
}
|
|
else if( !Q_stricmp( token, "noskills" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->noskills = Q_atoi( token ) ? true : false;
|
|
}
|
|
else if( !Q_stricmp( token, "render_picbutton_text" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->render_picbutton_text = Q_atoi( token ) ? true : false;
|
|
}
|
|
else if( !Q_stricmp( token, "internal_vgui_support" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->internal_vgui_support = Q_atoi( token ) ? true : false;
|
|
}
|
|
else if( !Q_stricmp( token, "quicksave_aged_count" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->quicksave_aged_count = bound( 2, Q_atoi( token ), 99 );
|
|
}
|
|
else if( !Q_stricmp( token, "autosave_aged_count" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
|
GameInfo->autosave_aged_count = bound( 2, Q_atoi( token ), 99 );
|
|
}
|
|
else if( !Q_stricmp( token, "demomap" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, GameInfo->demomap, sizeof( GameInfo->demomap ));
|
|
}
|
|
}
|
|
}
|
|
|
|
// demomap only valid for gameinfo.txt but HL1 after 25th anniversary update
|
|
// comes with demo chapter. Set the demomap here.
|
|
if( COM_StringEmpty( GameInfo->demomap ))
|
|
{
|
|
if( !Q_stricmp( GameInfo->title, "Half-Life" )) // original check from GameUI
|
|
Q_strncpy( GameInfo->demomap, "hldemo1", sizeof( GameInfo->demomap ));
|
|
}
|
|
|
|
if( !found_linux || !found_osx )
|
|
{
|
|
// just replace extension from dll to so/dylib
|
|
char gamedll[64];
|
|
Q_strncpy( gamedll, GameInfo->game_dll, sizeof( gamedll ));
|
|
COM_StripExtension( gamedll );
|
|
|
|
if( !found_linux )
|
|
Q_snprintf( GameInfo->game_dll_linux, sizeof( GameInfo->game_dll_linux ), "%s.so", gamedll );
|
|
|
|
if( !found_osx )
|
|
Q_snprintf( GameInfo->game_dll_osx, sizeof( GameInfo->game_dll_osx ), "%s.dylib", gamedll );
|
|
}
|
|
|
|
// make sure what gamedir is really exist
|
|
// a1ba: why we are doing this???
|
|
Q_snprintf( token, sizeof( token ), "%s/%s", fs_rootdir, GameInfo->falldir );
|
|
if( !FS_SysFolderExists( token ))
|
|
{
|
|
if( !COM_StringEmpty( fs_rodir ))
|
|
{
|
|
Q_snprintf( token, sizeof( token ), "%s/%s", fs_rodir, GameInfo->falldir );
|
|
if( !FS_SysFolderExists( token ))
|
|
GameInfo->falldir[0] = 0;
|
|
}
|
|
else GameInfo->falldir[0] = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
FS_ParseLiblistGam
|
|
================
|
|
*/
|
|
static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, gameinfo_t *GameInfo, time_t mtime )
|
|
{
|
|
char *afile = FS_LoadDirectFile( filename, NULL );
|
|
|
|
if( !afile )
|
|
return false;
|
|
|
|
FS_InitGameInfo( GameInfo, gamedir, false, mtime );
|
|
FS_ParseGenericGameInfo( GameInfo, afile, false );
|
|
|
|
Mem_Free( afile );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
FS_ConvertGameInfo
|
|
================
|
|
*/
|
|
static qboolean FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_path, const char *liblist_path, gameinfo_t *gi, time_t liblist_mtime )
|
|
{
|
|
memset( gi, 0, sizeof( *gi ));
|
|
|
|
// liblist.gam to gameinfo.txt conversion is deprecated, only support for RwDir!
|
|
if( FS_ParseLiblistGam( liblist_path, gamedir, gi, liblist_mtime ))
|
|
{
|
|
Con_DPrintf( "Convert %s to %s\n", liblist_path, gameinfo_path );
|
|
return FS_WriteGameInfo( gameinfo_path, gi );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
FS_ReadGameInfo
|
|
================
|
|
*/
|
|
static qboolean FS_ReadGameInfo( const char *filename, const char *gamedir, gameinfo_t *GameInfo, time_t mtime )
|
|
{
|
|
char *afile = FS_LoadDirectFile( filename, NULL );
|
|
|
|
if( !afile )
|
|
return false;
|
|
|
|
FS_InitGameInfo( GameInfo, gamedir, false, mtime );
|
|
FS_ParseGenericGameInfo( GameInfo, afile, true );
|
|
|
|
Mem_Free( afile );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
FS_CheckForQuakeGameDir
|
|
|
|
Checks if game directory resembles Quake Engine game directory
|
|
(some of checks may as well work with Xash gamedirs, it's not a bug)
|
|
================
|
|
*/
|
|
static qboolean FS_CheckForQuakeGameDir( const char *gamedir )
|
|
{
|
|
// if directory contain quake.rc or progs.dat it's 100% quake gamedir
|
|
// quake mods probably always archived, so check pak0.pak too
|
|
const char *files[] = { "pak0.pak", "PAK0.PAK", "progs.dat", "quake.rc" };
|
|
|
|
// search it in the filesystem
|
|
for( int i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ )
|
|
{
|
|
char buf[MAX_SYSPATH];
|
|
|
|
if( Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] ) > 0 )
|
|
{
|
|
if( !FS_SysFileExists( buf ))
|
|
continue;
|
|
|
|
if( !Q_stricmp( COM_FileExtension( buf ), "pak" ))
|
|
{
|
|
if( FS_CheckForQuakePak( buf, &files[2], sizeof( files ) / sizeof( files[0] ) - 2 ))
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
FS_CheckForXashGameDir
|
|
|
|
Checks if game directory resembles Xash3D game directory
|
|
===============
|
|
*/
|
|
static qboolean FS_CheckForXashGameDir( const char *gamedir )
|
|
{
|
|
// if directory contain gameinfo.txt or liblist.gam it's 100% gamedir
|
|
const char *files[] = { "gameinfo.txt", "liblist.gam" };
|
|
|
|
for( int i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ )
|
|
{
|
|
char buf[MAX_SYSPATH];
|
|
|
|
if( Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] ) > 0 )
|
|
{
|
|
if( FS_SysFileExists( buf ))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
FS_ParseGameInfo
|
|
================
|
|
*/
|
|
qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo, qboolean rodir )
|
|
{
|
|
char liblist_path[MAX_SYSPATH];
|
|
char gameinfo_path[MAX_SYSPATH];
|
|
char gamedir_path[MAX_SYSPATH];
|
|
time_t liblist_mtime;
|
|
time_t gameinfo_mtime;
|
|
|
|
if( rodir )
|
|
Q_snprintf( gamedir_path, sizeof( gamedir_path ), "%s/%s", fs_rodir, gamedir );
|
|
else
|
|
Q_snprintf( gamedir_path, sizeof( gamedir_path ), "%s", gamedir );
|
|
|
|
if( !FS_CheckForXashGameDir( gamedir_path ))
|
|
{
|
|
// check if we need to generate gameinfo for Quake
|
|
if( FS_CheckForQuakeGameDir( gamedir_path ))
|
|
{
|
|
// just generate stub gameinfo in memory
|
|
FS_InitGameInfo( GameInfo, gamedir, true, -1 );
|
|
GameInfo->rodir = rodir;
|
|
return true;
|
|
}
|
|
|
|
// don't add empty or addon directories
|
|
return false;
|
|
}
|
|
|
|
Q_snprintf( gameinfo_path, sizeof( gameinfo_path ), "%s/gameinfo.txt", gamedir_path );
|
|
Q_snprintf( liblist_path, sizeof( liblist_path ), "%s/liblist.gam", gamedir_path );
|
|
|
|
liblist_mtime = FS_SysFileTime( liblist_path );
|
|
gameinfo_mtime = FS_SysFileTime( gameinfo_path );
|
|
|
|
// in this function we never write new files for RoDir compatibility
|
|
// since RoDir is only FWGS feature, do gameinfo.txt conversion only
|
|
// for RwDir for those who worked with original Xash3D
|
|
if( !rodir )
|
|
{
|
|
// !!!only if we have both liblist.gam and gameinfo.txt try to convert liblist.gam to gameinfo.txt if it's newer!!!
|
|
if( liblist_mtime >= 0 && gameinfo_mtime >= 0 && liblist_mtime > gameinfo_mtime )
|
|
{
|
|
if( FS_ConvertGameInfo( gamedir, gameinfo_path, liblist_path, GameInfo, liblist_mtime ))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// can we parse gameinfo.txt?
|
|
if( gameinfo_mtime >= 0 )
|
|
{
|
|
if( FS_ReadGameInfo( gameinfo_path, gamedir, GameInfo, gameinfo_mtime ))
|
|
{
|
|
GameInfo->rodir = rodir;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// can we parse liblist.gam?
|
|
if( liblist_mtime >= 0 )
|
|
{
|
|
if( FS_ParseLiblistGam( liblist_path, gamedir, GameInfo, liblist_mtime ))
|
|
{
|
|
GameInfo->rodir = rodir;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|