mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
filesystem: refactor large filesystem.c into smaller files
This commit is contained in:
@@ -433,7 +433,7 @@ public:
|
||||
|
||||
bool GetCurrentDirectory( char *p, int size ) override
|
||||
{
|
||||
return FS_GetRootDirectory( p, size );
|
||||
return g_api.GetRootDirectory( p, size );
|
||||
}
|
||||
|
||||
void PrintOpenedFiles() override
|
||||
|
||||
237
filesystem/dll.c
Normal file
237
filesystem/dll.c
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
dll.c - filesystem_stdio DLL entry, engine interface stubs, exported API table
|
||||
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 <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include "port.h"
|
||||
#include "crtlib.h"
|
||||
#include "filesystem.h"
|
||||
#include "filesystem_internal.h"
|
||||
#include "common/com_strings.h"
|
||||
|
||||
static poolhandle_t Mem_AllocPoolStub( const char *name, unsigned int flags, const char *filename, int fileline )
|
||||
{
|
||||
return (poolhandle_t)0xDEADC0DE;
|
||||
}
|
||||
|
||||
static void Mem_FreePoolStub( poolhandle_t *poolptr, const char *filename, int fileline )
|
||||
{
|
||||
// stub
|
||||
}
|
||||
|
||||
static void *Mem_AllocStub( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline )
|
||||
{
|
||||
void *ptr = malloc( size );
|
||||
if( clear ) memset( ptr, 0, size );
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void *Mem_ReallocStub( poolhandle_t poolptr, void *memptr, size_t size, qboolean clear, const char *filename, int fileline )
|
||||
{
|
||||
return realloc( memptr, size );
|
||||
}
|
||||
|
||||
static void Mem_FreeStub( void *data, const char *filename, int fileline )
|
||||
{
|
||||
free( data );
|
||||
}
|
||||
|
||||
static void Con_PrintfStub( const char *fmt, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start( ap, fmt );
|
||||
vprintf( fmt, ap );
|
||||
va_end( ap );
|
||||
}
|
||||
|
||||
static void Sys_ErrorStub( const char *fmt, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start( ap, fmt );
|
||||
vfprintf( stderr, fmt, ap );
|
||||
va_end( ap );
|
||||
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
static qboolean FS_GetRootDirectory( char *path, size_t size )
|
||||
{
|
||||
size_t dirlen = Q_strlen( fs_rootdir );
|
||||
|
||||
if( dirlen >= size ) // check for possible overflow
|
||||
return false;
|
||||
|
||||
Q_strncpy( path, fs_rootdir, size );
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *Sys_GetNativeObjectStub( const char *object )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fs_interface_t g_engfuncs =
|
||||
{
|
||||
Con_PrintfStub,
|
||||
Con_PrintfStub,
|
||||
Con_PrintfStub,
|
||||
Sys_ErrorStub,
|
||||
Mem_AllocPoolStub,
|
||||
Mem_FreePoolStub,
|
||||
Mem_AllocStub,
|
||||
Mem_ReallocStub,
|
||||
Mem_FreeStub,
|
||||
Sys_GetNativeObjectStub,
|
||||
};
|
||||
|
||||
static qboolean FS_InitInterface( int version, const fs_interface_t *engfuncs )
|
||||
{
|
||||
// to be extended in future interface revisions
|
||||
if( version != FS_API_VERSION )
|
||||
{
|
||||
Con_Printf( S_ERROR "filesystem optional interface version mismatch: expected %d, got %d\n",
|
||||
FS_API_VERSION, version );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( engfuncs->_Con_Printf )
|
||||
g_engfuncs._Con_Printf = engfuncs->_Con_Printf;
|
||||
|
||||
if( engfuncs->_Con_DPrintf )
|
||||
g_engfuncs._Con_DPrintf = engfuncs->_Con_DPrintf;
|
||||
|
||||
if( engfuncs->_Con_Reportf )
|
||||
g_engfuncs._Con_Reportf = engfuncs->_Con_Reportf;
|
||||
|
||||
if( engfuncs->_Sys_Error )
|
||||
g_engfuncs._Sys_Error = engfuncs->_Sys_Error;
|
||||
|
||||
if( engfuncs->_Mem_AllocPool && engfuncs->_Mem_FreePool )
|
||||
{
|
||||
g_engfuncs._Mem_AllocPool = engfuncs->_Mem_AllocPool;
|
||||
g_engfuncs._Mem_FreePool = engfuncs->_Mem_FreePool;
|
||||
|
||||
Con_Reportf( "filesystem_stdio: custom pool allocation functions found\n" );
|
||||
}
|
||||
|
||||
if( engfuncs->_Mem_Alloc && engfuncs->_Mem_Realloc && engfuncs->_Mem_Free )
|
||||
{
|
||||
g_engfuncs._Mem_Alloc = engfuncs->_Mem_Alloc;
|
||||
g_engfuncs._Mem_Realloc = engfuncs->_Mem_Realloc;
|
||||
g_engfuncs._Mem_Free = engfuncs->_Mem_Free;
|
||||
|
||||
Con_Reportf( "filesystem_stdio: custom memory allocation functions found\n" );
|
||||
}
|
||||
|
||||
if( engfuncs->_Sys_GetNativeObject )
|
||||
{
|
||||
g_engfuncs._Sys_GetNativeObject = engfuncs->_Sys_GetNativeObject;
|
||||
Con_Reportf( "filesystem_stdio: custom platform-specific functions found\n" );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const fs_api_t g_api =
|
||||
{
|
||||
FS_InitStdio,
|
||||
FS_ShutdownStdio,
|
||||
|
||||
// search path utils
|
||||
FS_Rescan,
|
||||
FS_ClearSearchPath,
|
||||
FS_AllowDirectPaths,
|
||||
FS_AddGameDirectory,
|
||||
FS_AddGameHierarchy,
|
||||
FS_Search,
|
||||
FS_SetCurrentDirectory,
|
||||
FS_FindLibrary,
|
||||
FS_Path_f,
|
||||
|
||||
// gameinfo utils
|
||||
FS_Gamedir,
|
||||
FS_LoadGameInfo,
|
||||
|
||||
// file ops
|
||||
FS_Open,
|
||||
FS_Write,
|
||||
FS_Read,
|
||||
FS_Seek,
|
||||
FS_Tell,
|
||||
FS_Eof,
|
||||
FS_Flush,
|
||||
FS_Close,
|
||||
FS_Gets,
|
||||
FS_UnGetc,
|
||||
FS_Getc,
|
||||
FS_VPrintf,
|
||||
FS_Printf,
|
||||
FS_Print,
|
||||
FS_FileLength,
|
||||
FS_FileCopy,
|
||||
|
||||
// file buffer ops
|
||||
FS_LoadFile,
|
||||
FS_LoadDirectFile,
|
||||
FS_WriteFile,
|
||||
|
||||
// file hashing
|
||||
CRC32_File,
|
||||
MD5_HashFile,
|
||||
|
||||
// filesystem ops
|
||||
FS_FileExists,
|
||||
FS_FileTime,
|
||||
FS_FileSize,
|
||||
FS_Rename,
|
||||
FS_Delete,
|
||||
FS_SysFileExists,
|
||||
FS_GetDiskPath,
|
||||
|
||||
NULL,
|
||||
(void *)FS_MountArchive_Fullpath,
|
||||
|
||||
FS_GetFullDiskPath,
|
||||
FS_LoadFileMalloc,
|
||||
|
||||
FS_IsArchiveExtensionSupported,
|
||||
FS_GetArchiveByName,
|
||||
FS_FindFileInArchive,
|
||||
FS_OpenFileFromArchive,
|
||||
FS_LoadFileFromArchive,
|
||||
|
||||
FS_GetRootDirectory,
|
||||
|
||||
FS_MakeGameInfo,
|
||||
};
|
||||
|
||||
int EXPORT GetFSAPI( int version, fs_api_t *api, fs_globals_t **globals, fs_interface_t *engfuncs );
|
||||
int EXPORT GetFSAPI( int version, fs_api_t *api, fs_globals_t **globals, fs_interface_t *engfuncs )
|
||||
{
|
||||
if( engfuncs && !FS_InitInterface( version, engfuncs ))
|
||||
return 0;
|
||||
|
||||
*api = g_api;
|
||||
*globals = &FI;
|
||||
|
||||
return FS_API_VERSION;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -122,12 +122,12 @@ typedef struct searchpath_s
|
||||
|
||||
typedef searchpath_t *(*FS_ADDARCHIVE_FULLPATH)( const char *path, int flags );
|
||||
|
||||
extern fs_globals_t FI;
|
||||
extern searchpath_t *fs_writepath;
|
||||
extern poolhandle_t fs_mempool;
|
||||
extern char fs_rootdir[MAX_SYSPATH], fs_basedir[MAX_SYSPATH], fs_rodir[MAX_SYSPATH];
|
||||
extern fs_globals_t FI;
|
||||
extern searchpath_t *fs_writepath, *fs_searchpaths;
|
||||
extern poolhandle_t fs_mempool;
|
||||
extern fs_interface_t g_engfuncs;
|
||||
extern char fs_rootdir[MAX_SYSPATH];
|
||||
extern const fs_api_t g_api;
|
||||
extern const fs_api_t g_api;
|
||||
|
||||
#define GI FI.GameInfo
|
||||
|
||||
@@ -148,82 +148,94 @@ extern const fs_api_t g_api;
|
||||
//
|
||||
// filesystem.c
|
||||
//
|
||||
qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir );
|
||||
void FS_ShutdownStdio( void );
|
||||
searchpath_t *FS_MountArchive_Fullpath( const char *file, int flags );
|
||||
void FS_InitMemory( void );
|
||||
void _Mem_Free( void *data, const char *filename, int fileline );
|
||||
void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline )
|
||||
ALLOC_CHECK( 2 ) MALLOC_LIKE( _Mem_Free, 1 ) WARN_UNUSED_RESULT;
|
||||
void FS_EnsureOpenFile( file_t *file );
|
||||
void FS_BackupFileName( file_t *file, const char *path, uint options );
|
||||
|
||||
// search path utils
|
||||
void FS_Rescan( uint32_t flags, const char *language );
|
||||
void FS_ClearSearchPath( void );
|
||||
void FS_AllowDirectPaths( qboolean enable );
|
||||
//
|
||||
// searchpath.c
|
||||
//
|
||||
searchpath_t *FS_MountArchive_Fullpath( const char *file, int flags );
|
||||
void FS_AddGameDirectory( const char *dir, uint flags );
|
||||
void FS_ClearSearchPath( void );
|
||||
int FS_CheckNastyPath( const char *path );
|
||||
void FS_AddGameHierarchy( const char *dir, uint flags );
|
||||
search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly )
|
||||
MALLOC_LIKE( _Mem_Free, 1 ) WARN_UNUSED_RESULT;
|
||||
int FS_SetCurrentDirectory( const char *path );
|
||||
qboolean FS_GetRootDirectory( char *path, size_t size );
|
||||
void FS_Rescan( uint32_t flags, const char *language );
|
||||
const char *FS_Gamedir( void );
|
||||
void FS_LoadGameInfo( uint32_t flags, const char *language );
|
||||
qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir );
|
||||
void FS_AllowDirectPaths( qboolean enable );
|
||||
void FS_ShutdownStdio( void );
|
||||
void FS_Path_f( void );
|
||||
searchpath_t *FS_FindFile( const char *name, int *index, char *fixedname, size_t len, uint32_t flags );
|
||||
qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dllinfo_t *dllInfo );
|
||||
qboolean FS_FullPathToRelativePath( char *dst, const char *src, size_t size );
|
||||
search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) MALLOC_LIKE( _Mem_Free, 1 ) WARN_UNUSED_RESULT;
|
||||
qboolean FS_IsArchiveExtensionSupported( const char *ext, uint flags );
|
||||
searchpath_t *FS_GetArchiveByName( const char *name, searchpath_t *prev );
|
||||
int FS_FindFileInArchive( searchpath_t *sp, const char *path, char *truepath, size_t len );
|
||||
file_t *FS_OpenFileFromArchive( searchpath_t *sp, const char *path, const char *mode, int pack_ind );
|
||||
|
||||
// file ops
|
||||
int FS_Close( file_t *file );
|
||||
file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly )
|
||||
MALLOC_LIKE( FS_Close, 1 ) WARN_UNUSED_RESULT;
|
||||
fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize );
|
||||
fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize );
|
||||
int FS_Seek( file_t *file, fs_offset_t offset, int whence );
|
||||
fs_offset_t FS_Tell( const file_t *file );
|
||||
qboolean FS_Eof( const file_t *file );
|
||||
int FS_Flush( file_t *file );
|
||||
int FS_Gets( file_t *file, char *string, size_t bufsize );
|
||||
int FS_UnGetc( file_t *file, char c );
|
||||
int FS_Getc( file_t *file );
|
||||
int FS_VPrintf( file_t *file, const char *format, va_list ap );
|
||||
int FS_Printf( file_t *file, const char *format, ... ) FORMAT_CHECK( 2 );
|
||||
int FS_Print( file_t *file, const char *msg );
|
||||
fs_offset_t FS_FileLength( const file_t *f );
|
||||
qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize );
|
||||
//
|
||||
// gameinfo.c
|
||||
//
|
||||
void FS_MakeGameInfo( void );
|
||||
qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo, qboolean rodir );
|
||||
|
||||
// file buffer ops
|
||||
byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly )
|
||||
MALLOC_LIKE( _Mem_Free, 1 ) WARN_UNUSED_RESULT;
|
||||
byte *FS_LoadFileMalloc( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly )
|
||||
MALLOC_LIKE( free, 1 ) WARN_UNUSED_RESULT;
|
||||
byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr )
|
||||
MALLOC_LIKE( _Mem_Free, 1 ) WARN_UNUSED_RESULT;
|
||||
qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len );
|
||||
|
||||
// file hashing
|
||||
qboolean CRC32_File( dword *crcvalue, const char *filename );
|
||||
qboolean MD5_HashFile( byte digest[16], const char *pszFileName, uint seed[4] );
|
||||
|
||||
// stringlist ops
|
||||
//
|
||||
// sys.c
|
||||
//
|
||||
void stringlistinit( stringlist_t *list );
|
||||
void stringlistfreecontents( stringlist_t *list );
|
||||
void stringlistappend( stringlist_t *list, const char *text );
|
||||
void stringlistsort( stringlist_t *list );
|
||||
void listdirectory( stringlist_t *list, const char *path, qboolean dirs_only );
|
||||
|
||||
// filesystem ops
|
||||
int FS_FileExists( const char *filename, int gamedironly );
|
||||
int FS_FileTime( const char *filename, qboolean gamedironly );
|
||||
fs_offset_t FS_FileSize( const char *filename, qboolean gamedironly );
|
||||
qboolean FS_Rename( const char *oldname, const char *newname );
|
||||
qboolean FS_Delete( const char *path );
|
||||
qboolean FS_SysFileExists( const char *path );
|
||||
const char *FS_GetDiskPath( const char *name, qboolean gamedironly );
|
||||
qboolean FS_GetFullDiskPath( char *buffer, size_t size, const char *name, qboolean gamedironly );
|
||||
void FS_CreatePath( char *path );
|
||||
int FS_SysFileTime( const char *filename );
|
||||
file_t *FS_SysOpen( const char *filepath, const char *mode );
|
||||
file_t *FS_OpenHandle( searchpath_t *search, int handle, fs_offset_t offset, fs_offset_t len );
|
||||
qboolean FS_SysFileExists( const char *path );
|
||||
qboolean FS_SysFolderExists( const char *path );
|
||||
qboolean FS_SysFileOrFolderExists( const char *path );
|
||||
file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedironly );
|
||||
int FS_SetCurrentDirectory( const char *path );
|
||||
|
||||
int FS_SysFileTime( const char *filename );
|
||||
file_t *FS_OpenHandle( searchpath_t *search, int handle, fs_offset_t offset, fs_offset_t len );
|
||||
file_t *FS_SysOpen( const char *filepath, const char *mode );
|
||||
qboolean FS_FullPathToRelativePath( char *dst, const char *src, size_t size );
|
||||
//
|
||||
// io.c
|
||||
//
|
||||
file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedironly );
|
||||
int FS_Close( file_t *file );
|
||||
file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly ) MALLOC_LIKE( FS_Close, 1 ) WARN_UNUSED_RESULT;
|
||||
int FS_Flush( file_t *file );
|
||||
fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize );
|
||||
fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize );
|
||||
int FS_Print( file_t *file, const char *msg );
|
||||
int FS_Printf( file_t *file, const char *format, ... ) FORMAT_CHECK( 2 );
|
||||
int FS_VPrintf( file_t *file, const char *format, va_list ap );
|
||||
int FS_Getc( file_t *file );
|
||||
int FS_UnGetc( file_t *file, char c );
|
||||
int FS_Gets( file_t *file, char *string, size_t bufsize );
|
||||
int FS_Seek( file_t *file, fs_offset_t offset, int whence );
|
||||
fs_offset_t FS_Tell( const file_t *file );
|
||||
qboolean FS_Eof( const file_t *file );
|
||||
byte *FS_LoadFileFromArchive( searchpath_t *sp, const char *path, int pack_ind, fs_offset_t *filesizeptr, const qboolean sys_malloc );
|
||||
byte *FS_LoadFileMalloc( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly ) MALLOC_LIKE( free, 1 ) WARN_UNUSED_RESULT;
|
||||
byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly ) MALLOC_LIKE( _Mem_Free, 1 ) WARN_UNUSED_RESULT;
|
||||
qboolean CRC32_File( dword *crcvalue, const char *filename );
|
||||
qboolean MD5_HashFile( byte digest[16], const char *pszFileName, uint seed[4] );
|
||||
byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr ) MALLOC_LIKE( _Mem_Free, 1 ) WARN_UNUSED_RESULT;
|
||||
qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len );
|
||||
int FS_FileExists( const char *filename, int gamedironly );
|
||||
const char *FS_GetDiskPath( const char *name, qboolean gamedironly );
|
||||
qboolean FS_GetFullDiskPath( char *buffer, size_t size, const char *name, qboolean gamedironly );
|
||||
fs_offset_t FS_FileSize( const char *filename, qboolean gamedironly );
|
||||
fs_offset_t FS_FileLength( const file_t *f );
|
||||
int FS_FileTime( const char *filename, qboolean gamedironly );
|
||||
qboolean FS_Rename( const char *oldname, const char *newname );
|
||||
qboolean FS_Delete( const char *path );
|
||||
qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize );
|
||||
|
||||
//
|
||||
// pak.c
|
||||
|
||||
699
filesystem/gameinfo.c
Normal file
699
filesystem/gameinfo.c
Normal file
@@ -0,0 +1,699 @@
|
||||
/*
|
||||
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 i, 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( 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" };
|
||||
int i;
|
||||
|
||||
// search it in the filesystem
|
||||
for( 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" };
|
||||
int i;
|
||||
|
||||
for( 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 = -1;
|
||||
time_t gameinfo_mtime = -1;
|
||||
|
||||
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;
|
||||
}
|
||||
1067
filesystem/io.c
Normal file
1067
filesystem/io.c
Normal file
File diff suppressed because it is too large
Load Diff
1089
filesystem/searchpath.c
Normal file
1089
filesystem/searchpath.c
Normal file
File diff suppressed because it is too large
Load Diff
527
filesystem/sys.c
Normal file
527
filesystem/sys.c
Normal file
@@ -0,0 +1,527 @@
|
||||
/*
|
||||
sys.c - stringlist, directory walk, raw OS file I/O 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.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
|
||||
#include "build.h"
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if XASH_WIN32
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
#include "utflib.h"
|
||||
#elif XASH_DOS4GW
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_MEMFD_CREATE
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "port.h"
|
||||
#include "crtlib.h"
|
||||
#include "filesystem.h"
|
||||
#include "filesystem_internal.h"
|
||||
#include "common/com_strings.h"
|
||||
|
||||
#if !defined( O_BINARY )
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
#if !defined( O_TEXT )
|
||||
#define O_TEXT 0
|
||||
#endif
|
||||
|
||||
#if !defined( MFD_NOEXEC_SEAL )
|
||||
#define MFD_NOEXEC_SEAL 8U
|
||||
#endif
|
||||
|
||||
#if !defined( S_ISREG )
|
||||
#define S_ISREG( m ) ( FBitSet( m, S_IFMT ) == S_IFREG )
|
||||
#endif
|
||||
|
||||
#if !defined( S_ISDIR )
|
||||
#define S_ISDIR( m ) ( FBitSet( m, S_IFMT ) == S_IFDIR )
|
||||
#endif
|
||||
|
||||
#if !XASH_PSVITA && !XASH_NSWITCH
|
||||
#define HAVE_DUP
|
||||
#endif
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
FILEMATCH COMMON SYSTEM
|
||||
|
||||
=============================================================================
|
||||
*/
|
||||
void stringlistinit( stringlist_t *list )
|
||||
{
|
||||
memset( list, 0, sizeof( *list ));
|
||||
}
|
||||
|
||||
void stringlistfreecontents( stringlist_t *list )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < list->numstrings; i++ )
|
||||
{
|
||||
if( list->strings[i] )
|
||||
Mem_Free( list->strings[i] );
|
||||
list->strings[i] = NULL;
|
||||
}
|
||||
|
||||
if( list->strings )
|
||||
Mem_Free( list->strings );
|
||||
|
||||
list->numstrings = 0;
|
||||
list->maxstrings = 0;
|
||||
list->strings = NULL;
|
||||
}
|
||||
|
||||
void stringlistappend( stringlist_t *list, const char *text )
|
||||
{
|
||||
size_t textlen;
|
||||
|
||||
if( !Q_strcmp( text, "." ) || !Q_strcmp( text, ".." ))
|
||||
return; // ignore the virtual directories
|
||||
|
||||
if( list->numstrings >= list->maxstrings )
|
||||
{
|
||||
list->maxstrings += 4096;
|
||||
list->strings = Mem_Realloc( fs_mempool, list->strings, list->maxstrings * sizeof( *list->strings ));
|
||||
}
|
||||
|
||||
textlen = Q_strlen( text ) + 1;
|
||||
list->strings[list->numstrings] = Mem_Calloc( fs_mempool, textlen );
|
||||
memcpy( list->strings[list->numstrings], text, textlen );
|
||||
list->numstrings++;
|
||||
}
|
||||
|
||||
void stringlistsort( stringlist_t *list )
|
||||
{
|
||||
char *temp;
|
||||
int i, j;
|
||||
|
||||
// this is a selection sort (finds the best entry for each slot)
|
||||
for( i = 0; i < list->numstrings - 1; i++ )
|
||||
{
|
||||
for( j = i + 1; j < list->numstrings; j++ )
|
||||
{
|
||||
if( Q_strcmp( list->strings[i], list->strings[j] ) > 0 )
|
||||
{
|
||||
temp = list->strings[i];
|
||||
list->strings[i] = list->strings[j];
|
||||
list->strings[j] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert names to lowercase because dos doesn't care, but pattern matching code often does
|
||||
MAYBE_UNUSED static void listlowercase( stringlist_t *list )
|
||||
{
|
||||
char *c;
|
||||
int i;
|
||||
|
||||
for( i = 0; i < list->numstrings; i++ )
|
||||
{
|
||||
for( c = list->strings[i]; *c; c++ )
|
||||
*c = Q_tolower( *c );
|
||||
}
|
||||
}
|
||||
|
||||
void listdirectory( stringlist_t *list, const char *path, qboolean dirs_only )
|
||||
{
|
||||
#if XASH_WIN32
|
||||
char pattern[4096];
|
||||
Q_snprintf( pattern, sizeof( pattern ), "%s/*", path );
|
||||
|
||||
// ask for the directory listing handle
|
||||
struct _finddata_t n_file = { 0 };
|
||||
intptr_t hFile = _findfirst( pattern, &n_file );
|
||||
if( hFile == -1 )
|
||||
return;
|
||||
|
||||
// start a new chain with the the first name
|
||||
stringlistappend( list, n_file.name );
|
||||
|
||||
// iterate through the directory
|
||||
while( _findnext( hFile, &n_file ) == 0 )
|
||||
{
|
||||
if( dirs_only && !FBitSet( n_file.attrib, _A_SUBDIR ))
|
||||
continue;
|
||||
|
||||
stringlistappend( list, n_file.name );
|
||||
}
|
||||
|
||||
_findclose( hFile );
|
||||
#else // !XASH_WIN32
|
||||
DIR *dir = opendir( path );
|
||||
|
||||
if( !dir )
|
||||
return;
|
||||
|
||||
// iterate through the directory
|
||||
struct dirent *entry;
|
||||
while(( entry = readdir( dir )))
|
||||
{
|
||||
#if HAVE_DIRENT_D_TYPE
|
||||
if( dirs_only && entry->d_type != DT_DIR && entry->d_type != DT_LNK && entry->d_type != DT_UNKNOWN )
|
||||
continue;
|
||||
#endif // HAVE_DIRENT_D_TYPE
|
||||
|
||||
stringlistappend( list, entry->d_name );
|
||||
}
|
||||
|
||||
closedir( dir );
|
||||
#endif // !XASH_WIN32
|
||||
|
||||
#if XASH_DOS4GW
|
||||
// convert names to lowercase because 8.3 always in CAPS
|
||||
listlowercase( list );
|
||||
#endif // XASH_DOS4GW
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
FS_PathToWideChar
|
||||
|
||||
Converts input UTF-8 string to wide char string.
|
||||
====================
|
||||
*/
|
||||
MAYBE_UNUSED static const wchar_t *FS_PathToWideChar( const char *path )
|
||||
{
|
||||
#if XASH_WIN32
|
||||
static wchar_t pathBuffer[MAX_PATH];
|
||||
MultiByteToWideChar( CP_UTF8, 0, path, -1, pathBuffer, MAX_PATH );
|
||||
return pathBuffer;
|
||||
#endif
|
||||
return L"";
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
FS_CreatePath
|
||||
|
||||
Only used for FS_Open.
|
||||
============
|
||||
*/
|
||||
void FS_CreatePath( char *path )
|
||||
{
|
||||
char *ofs, save;
|
||||
|
||||
for( ofs = path + 1; *ofs; ofs++ )
|
||||
{
|
||||
if( *ofs == '/' || *ofs == '\\' )
|
||||
{
|
||||
// create the directory
|
||||
save = *ofs;
|
||||
*ofs = 0;
|
||||
#if XASH_WIN32
|
||||
_mkdir( path ); // use _wmkdir maybe?
|
||||
#else // !XASH_WIN32
|
||||
mkdir( path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );
|
||||
#endif // !XASH_WIN32
|
||||
*ofs = save;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
FS_SysFileTime
|
||||
|
||||
Internal function used to determine filetime
|
||||
====================
|
||||
*/
|
||||
int FS_SysFileTime( const char *filename )
|
||||
{
|
||||
#if XASH_WIN32
|
||||
struct _stat buf;
|
||||
if( _wstat( FS_PathToWideChar( filename ), &buf ) < 0 )
|
||||
#else
|
||||
struct stat buf;
|
||||
if( stat( filename, &buf ) < 0 )
|
||||
#endif
|
||||
return -1;
|
||||
|
||||
return buf.st_mtime;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
FS_SysFileExists
|
||||
|
||||
Look for a file in the filesystem only
|
||||
==================
|
||||
*/
|
||||
qboolean FS_SysFileExists( const char *path )
|
||||
{
|
||||
#if XASH_WIN32
|
||||
struct _stat buf;
|
||||
if( _wstat( FS_PathToWideChar( path ), &buf ) < 0 )
|
||||
#else // !XASH_WIN32
|
||||
struct stat buf;
|
||||
if( stat( path, &buf ) < 0 )
|
||||
#endif // !XASH_WIN32
|
||||
return false;
|
||||
|
||||
return S_ISREG( buf.st_mode );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
FS_SysFolderExists
|
||||
|
||||
Look for a existing folder
|
||||
==================
|
||||
*/
|
||||
qboolean FS_SysFolderExists( const char *path )
|
||||
{
|
||||
#if XASH_WIN32
|
||||
struct _stat buf;
|
||||
if( _wstat( FS_PathToWideChar( path ), &buf ) < 0 )
|
||||
#else
|
||||
struct stat buf;
|
||||
if( stat( path, &buf ) < 0 )
|
||||
#endif
|
||||
return false;
|
||||
|
||||
return S_ISDIR( buf.st_mode );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
FS_SysFileOrFolderExists
|
||||
|
||||
Check if filesystem entry exists at all, don't mind the type
|
||||
==============
|
||||
*/
|
||||
qboolean FS_SysFileOrFolderExists( const char *path )
|
||||
{
|
||||
#if XASH_WIN32
|
||||
struct _stat buf;
|
||||
return _wstat( FS_PathToWideChar( path ), &buf ) >= 0;
|
||||
#else
|
||||
struct stat buf;
|
||||
return stat( path, &buf ) >= 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
FS_SysOpen
|
||||
|
||||
Internal function used to create a file_t and open the relevant non-packed file on disk
|
||||
====================
|
||||
*/
|
||||
file_t *FS_SysOpen( const char *filepath, const char *mode )
|
||||
{
|
||||
file_t *file;
|
||||
int mod, opt, fd = -1;
|
||||
qboolean memfile = false;
|
||||
uint ind;
|
||||
|
||||
// Parse the mode string
|
||||
switch( mode[0] )
|
||||
{
|
||||
case 'r': // read
|
||||
mod = O_RDONLY;
|
||||
opt = 0;
|
||||
break;
|
||||
case 'w': // write
|
||||
mod = O_WRONLY;
|
||||
opt = O_CREAT | O_TRUNC;
|
||||
break;
|
||||
case 'a': // append
|
||||
mod = O_WRONLY;
|
||||
opt = O_CREAT | O_APPEND;
|
||||
break;
|
||||
case 'e': // edit
|
||||
mod = O_WRONLY;
|
||||
opt = O_CREAT;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for( ind = 1; mode[ind] != '\0'; ind++ )
|
||||
{
|
||||
switch( mode[ind] )
|
||||
{
|
||||
case '+':
|
||||
mod = O_RDWR;
|
||||
break;
|
||||
case 'b':
|
||||
opt |= O_BINARY;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// the 'm' flag let's user to create temporary file in memory
|
||||
// through so-called "anonymous files"
|
||||
if( Q_strchr( mode, 'm' ))
|
||||
{
|
||||
#if HAVE_MEMFD_CREATE
|
||||
fd = memfd_create( filepath, MFD_CLOEXEC | MFD_NOEXEC_SEAL );
|
||||
|
||||
// through fcntl() and MFD_ALLOW_SEALING we could enforce
|
||||
// read-write flags but we don't really care about them yet
|
||||
if( fd < 0 )
|
||||
Con_Printf( S_WARN "%s: can't create anonymous file %s: %s\n", __func__, filepath, strerror( errno ));
|
||||
else
|
||||
memfile = true;
|
||||
#endif
|
||||
// if it's unsupported, we can open it on disk
|
||||
}
|
||||
|
||||
if( fd < 0 )
|
||||
{
|
||||
#if XASH_WIN32
|
||||
fd = _wopen( FS_PathToWideChar( filepath ), mod | opt, 0666 );
|
||||
#else // !XASH_WIN32
|
||||
fd = open( filepath, mod | opt, 0666 );
|
||||
#endif // !XASH_WIN32
|
||||
}
|
||||
|
||||
if( fd < 0 )
|
||||
{
|
||||
if( errno != ENOENT )
|
||||
Con_Printf( S_ERROR "%s: can't open file %s: %s\n", __func__, filepath, strerror( errno ));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file = (file_t *)Mem_Calloc( fs_mempool, sizeof( *file ));
|
||||
file->filetime = memfile ? 0 : FS_SysFileTime( filepath );
|
||||
file->ungetc = EOF;
|
||||
file->handle = fd;
|
||||
|
||||
if( !memfile )
|
||||
FS_BackupFileName( file, filepath, mod | opt );
|
||||
|
||||
file->searchpath = NULL;
|
||||
file->real_length = lseek( file->handle, 0, SEEK_END );
|
||||
|
||||
// uncomment do disable write
|
||||
//if( opt & O_CREAT )
|
||||
// return NULL;
|
||||
|
||||
// For files opened in append mode, we start at the end of the file
|
||||
if( opt & O_APPEND )
|
||||
file->position = file->real_length;
|
||||
else
|
||||
lseek( file->handle, 0, SEEK_SET );
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
FS_OpenHandle
|
||||
|
||||
====================
|
||||
*/
|
||||
file_t *FS_OpenHandle( searchpath_t *searchpath, int handle, fs_offset_t offset, fs_offset_t len )
|
||||
{
|
||||
file_t *file = (file_t *)Mem_Calloc( fs_mempool, sizeof( file_t ));
|
||||
|
||||
#ifdef XASH_REDUCE_FD
|
||||
file->backup_position = offset;
|
||||
file->backup_path = copystring( syspath );
|
||||
file->backup_options = O_RDONLY|O_BINARY;
|
||||
file->handle = -1;
|
||||
#else // !XASH_REDUCE_FD
|
||||
#ifdef HAVE_DUP
|
||||
file->handle = dup( handle );
|
||||
#else // !HAVE_DUP
|
||||
file->handle = open( searchpath->filename, O_RDONLY|O_BINARY );
|
||||
#endif // !HAVE_DUP
|
||||
|
||||
if( file->handle < 0 )
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: couldn't create fd for %s:0x%lx: %s\n", __func__, searchpath->filename, (long)offset, strerror( errno ));
|
||||
Mem_Free( file );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( lseek( file->handle, offset, SEEK_SET ) == -1 )
|
||||
{
|
||||
Mem_Free( file );
|
||||
return NULL;
|
||||
}
|
||||
#endif // !XASH_REDUCE_FD
|
||||
|
||||
file->real_length = len;
|
||||
file->offset = offset;
|
||||
file->position = 0;
|
||||
file->ungetc = EOF;
|
||||
file->searchpath = searchpath;
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
FS_SetCurrentDirectory
|
||||
|
||||
Sets current directory, path should be in UTF-8 encoding
|
||||
TODO: make this non-fatal
|
||||
==================
|
||||
*/
|
||||
int FS_SetCurrentDirectory( const char *path )
|
||||
{
|
||||
#if XASH_WIN32
|
||||
if( !SetCurrentDirectoryW( FS_PathToWideChar( path )))
|
||||
{
|
||||
const DWORD fm_flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK;
|
||||
DWORD errorcode;
|
||||
wchar_t wide_buf[1024];
|
||||
char buf[1024];
|
||||
|
||||
FormatMessageW( fm_flags, NULL, GetLastError(), 0, wide_buf, sizeof( wide_buf ) / sizeof( wide_buf[0] ), NULL );
|
||||
Q_UTF16ToUTF8( buf, sizeof( buf ), wide_buf, sizeof( wide_buf ) / sizeof( wide_buf[0] ));
|
||||
|
||||
Sys_Error( "Changing directory to %s failed: %s\n", path, buf );
|
||||
return false;
|
||||
}
|
||||
#elif XASH_POSIX
|
||||
if( chdir( path ) < 0 )
|
||||
{
|
||||
Sys_Error( "Changing directory to %s failed: %s\n", path, strerror( errno ));
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// it may be fine for some systems to skip chdir
|
||||
Con_Printf( "%s: not implemented, ignoring...\n", __func__ );
|
||||
return true;
|
||||
#endif
|
||||
|
||||
Con_Printf( "%s is working directory now\n", path );
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user