qboolean EndOfScript( qboolean crossline ){
if ( !crossline ) {
- Error( "Line %i is incomplete\n",scriptline );
+ Error( "Line %i is incomplete\nFile location be: %s\n", scriptline, g_strLoadedFileLocation );
}
if ( !strcmp( script->filename, "memory buffer" ) ) {
// skip space
//
skipspace:
- while ( *script->script_p <= 32 )
+ while ( script->script_p < script->end_p && *script->script_p <= 32 )
{
if ( script->script_p >= script->end_p ) {
return EndOfScript( crossline );
}
if ( *script->script_p++ == '\n' ) {
if ( !crossline ) {
- Error( "Line %i is incomplete\n",scriptline );
+ Error( "Line %i is incomplete\nFile location be: %s\n", scriptline, g_strLoadedFileLocation );
}
script->line++;
scriptline = script->line;
if ( *script->script_p == ';' || *script->script_p == '#'
|| ( script->script_p[0] == '/' && script->script_p[1] == '/' ) ) {
if ( !crossline ) {
- Error( "Line %i is incomplete\n",scriptline );
+ Error( "Line %i is incomplete\nFile location be: %s\n", scriptline, g_strLoadedFileLocation );
}
while ( *script->script_p++ != '\n' )
if ( script->script_p >= script->end_p ) {
// /* */ comments
if ( script->script_p[0] == '/' && script->script_p[1] == '*' ) {
if ( !crossline ) {
- Error( "Line %i is incomplete\n",scriptline );
+ Error( "Line %i is incomplete\nFile location be: %s\n", scriptline, g_strLoadedFileLocation );
}
script->script_p += 2;
while ( script->script_p[0] != '*' && script->script_p[1] != '/' )
break;
}
if ( token_p == &token[MAXTOKEN] ) {
- Error( "Token too large on line %i\n",scriptline );
+ Error( "Token too large on line %i\nFile location be: %s\n", scriptline, g_strLoadedFileLocation );
}
}
script->script_p++;
break;
}
if ( token_p == &token[MAXTOKEN] ) {
- Error( "Token too large on line %i\n",scriptline );
+ Error( "Token too large on line %i\nFile location be: %s\n", scriptline, g_strLoadedFileLocation );
}
}
}
#include "mathlib.h"
#include "inout.h"
#include "vfs.h"
-#include <minizip/unzip.h>
+#include <unzip.h>
#include <glib.h>
#define GARUX_DISABLE_BAD_MINIZ
#ifndef GARUX_DISABLE_BAD_MINIZ
typedef struct
{
+ char* unzFilePath;
char* name;
unzFile zipfile;
unz_file_pos zippos;
char g_strForbiddenDirs[VFS_MAXDIRS][PATH_MAX + 1];
int g_numForbiddenDirs = 0;
static gboolean g_bUsePak = TRUE;
+ char g_strLoadedFileLocation[1024];
// =============================================================================
// Static functions
}
unzGoToFirstFile( uf );
+ char* unzFilePath = strdup( filename );
+
for ( i = 0; i < gi.number_entry; i++ )
{
char filename_inzip[NAME_MAX];
file->name = strdup( filename_lower );
file->size = file_info.uncompressed_size;
file->zipfile = uf;
+ file->unzFilePath = unzFilePath;
file->zippos = pos;
if ( ( i + 1 ) < gi.number_entry ) {
}
if ( g_numDirs == VFS_MAXDIRS ) {
+ Sys_FPrintf( SYS_WRN, "WARNING: too many VFS directories, can't init %s\n", path );
return;
}
if ( ext != NULL && ( !Q_stricmp( ext, ".pk3dir" ) || !Q_stricmp( ext, ".dpkdir" ) ) ) {
if ( g_numDirs == VFS_MAXDIRS ) {
+ g_free( dirlist );
continue;
}
snprintf( g_strDirs[g_numDirs], PATH_MAX, "%s/%s", path, name );
}
if ( ext == NULL || ( Q_stricmp( ext, ".pk3" ) != 0 && Q_stricmp( ext, ".dpk" ) != 0 ) ) {
+ g_free( dirlist );
continue;
}
}
while ( g_pakFiles )
{
VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
+ free( file->unzFilePath );
free( file->name );
free( file );
g_pakFiles = g_slist_remove( g_pakFiles, file );
return count;
}
+static qboolean isSymlink(const unz_file_info64 *fileInfo) {
+ // see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/stat.h
+ // redefine so it works outside of Unices
+ const unsigned long Q3MAP_S_IFMT = 00170000;
+ const unsigned long Q3MAP_S_IFLNK = 0120000;
+ // see https://trac.edgewall.org/attachment/ticket/8919/ZipDownload.patch
+ const unsigned long PKZIP_EXTERNAL_ATTR_FILE_TYPE_SHIFT = 16;
+
+ unsigned long attr = fileInfo->external_fa >> PKZIP_EXTERNAL_ATTR_FILE_TYPE_SHIFT;
+ return (attr & Q3MAP_S_IFMT) == Q3MAP_S_IFLNK;
+}
+
+// The zip format has a maximum filename size of 64K
+static const int MAX_FILENAME_BUF = 65537;
+
+/* The symlink implementation is ported from Dæmon engine implementation by slipher which was a complete rewrite of one illwieckz did on Dæmon by taking inspiration from Darkplaces engine.
+
+See:
+
+- https://github.com/DaemonEngine/Daemon/blob/master/src/common/FileSystem.cpp
+- https://gitlab.com/xonotic/darkplaces/-/blob/div0-stable/fs.c
+
+Some words by slipher:
+
+> Symlinks are a bad feature which you should not use. Therefore, the implementation is as
+> slow as possible with a full iteration of the archive performed for each symlink.
+
+> The symlink path `relative` must be relative to the symlink's location.
+> Only supports paths consisting of "../" 0 or more times, followed by non-magical path components.
+*/
+void resolveSymlinkPath( const char* base, const char* relative, char* resolved ){
+
+ base = g_path_get_dirname( base );
+
+ while( g_str_has_prefix( relative, "../" ) )
+ {
+ if ( base[0] == '\0' )
+ {
+ Sys_FPrintf( SYS_WRN, "Error while reading symbolic link: \"%s\": no such directory\n", base );
+ resolved[0] = '\0';
+ return;
+ }
+
+ base = g_path_get_dirname( base );
+ relative += 3;
+ }
+
+ snprintf( resolved, MAX_FILENAME_BUF, "%s/%s", base, relative);
+}
+
// NOTE: when loading a file, you have to allocate one extra byte and set it to \0
int vfsLoadFile( const char *filename, void **bufferptr, int index ){
int i, count = 0;
// filename is a full path
if ( index == -1 ) {
+ strcpy( g_strLoadedFileLocation, filename );
long len;
FILE *f;
}
*bufferptr = NULL;
- strcpy( fixed, filename );
+ strncpy( fixed, filename, sizeof( fixed ) );
vfsFixDOSName( fixed );
lower = g_ascii_strdown( fixed, -1 );
strcat( tmp, filename );
if ( access( tmp, R_OK ) == 0 ) {
if ( count == index ) {
+ strcpy( g_strLoadedFileLocation, tmp );
+
long len;
FILE *f;
}
}
+ // Do not resolve more than 5 recursive symbolic links to
+ // prevent circular symbolic links.
+ int max_symlink_depth = 5;
+
+ openSymlinkTarget:
for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
{
VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
}
if ( count == index ) {
+ strcpy( g_strLoadedFileLocation, file->unzFilePath );
+ strcat( g_strLoadedFileLocation, " :: " );
+ strcat( g_strLoadedFileLocation, filename );
+
if ( unzGoToFilePos( file->zipfile, &file->zippos ) != UNZ_OK ) {
return -1;
return -1;
}
+ unz_file_info64 fileInfo;
+ if ( unzGetCurrentFileInfo64( file->zipfile, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0 ) != UNZ_OK ) {
+ return -1;
+ }
+
*bufferptr = safe_malloc( file->size + 1 );
// we need to end the buffer with a 0
( (char*) ( *bufferptr ) )[file->size] = 0;
i = unzReadCurrentFile( file->zipfile, *bufferptr, file->size );
unzCloseCurrentFile( file->zipfile );
+
+ if ( isSymlink( &fileInfo ) ) {
+ Sys_FPrintf( SYS_VRB, "Found symbolic link: \"%s\"\n", filename );
+
+ if ( max_symlink_depth == 0 ) {
+ Sys_FPrintf( SYS_WRN, "Maximum symbolic link depth reached\n" );
+ g_free( lower );
+ return -1;
+ }
+
+ max_symlink_depth--;
+
+ const char* relative = (const char*) *bufferptr;
+ char resolved[MAX_FILENAME_BUF];
+
+ resolveSymlinkPath( file->name, relative, resolved );
+
+ Sys_FPrintf( SYS_VRB, "Resolved symbolic link: \"%s\"\n", resolved );
+
+ g_free( lower );
+ strncpy( fixed, resolved, sizeof( fixed ) );
+ vfsFixDOSName( fixed );
+ lower = g_ascii_strdown( fixed, -1 );
+
+ // slow as possible full iteration of the archive
+ goto openSymlinkTarget;
+ }
+
if ( i < 0 ) {
+ g_free( lower );
return -1;
}
else{
count++;
}
+
g_free( lower );
return -1;
}
// to get PATH_MAX
#include <stdio.h>
-#if GDEF_OS_LINUX || GDEF_OS_MACOS
-#include <dirent.h>
-#include <unistd.h>
-#else
+
+#if GDEF_OS_WINDOWS
#include <wtypes.h>
#include <io.h>
#endif
#define S_ISDIR( mode ) ( mode & _S_IFDIR )
+#else // !GDEF_OS_WINDOWS
+#include <dirent.h>
+#include <unistd.h>
+#endif // !GDEF_OS_WINDOWS
+
+#ifndef PATH_MAX
#define PATH_MAX 260
+#endif // PATH_MAX
+
+// PATH_MAX
+#if defined( __FreeBSD__ )
+#include <sys/syslimits.h>
#endif
-#define VFS_MAXDIRS 64
+// Multiple pakpaths with many pk3dirs can lead
+// to high list of VFS directories
+#define VFS_MAXDIRS 256
void vfsInitDirectory( const char *path );
void vfsShutdown();
extern char g_strForbiddenDirs[VFS_MAXDIRS][PATH_MAX + 1];
extern int g_numForbiddenDirs;
+ extern char g_strLoadedFileLocation[1024];
#endif // _VFS_H_
numBSPDrawVerts = n;
numBSPDrawVertsBuffer = numBSPDrawVerts;
- bspDrawVerts = safe_malloc_info( sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer, "IncDrawVerts" );
-
- memset( bspDrawVerts, 0, n * sizeof( bspDrawVert_t ) );
+ bspDrawVerts = safe_malloc0_info( sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer, "IncDrawVerts" );
}
int numBSPDrawSurfacesBuffer = 0;
numBSPDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
- bspDrawSurfaces = safe_malloc_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
-
- memset( bspDrawSurfaces, 0, MAX_MAP_DRAW_SURFS * sizeof( bspDrawSurface_t ) );
+ bspDrawSurfaces = safe_malloc0_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
}
void SetDrawSurfaces( int n ){
numBSPDrawSurfaces = n;
numBSPDrawSurfacesBuffer = numBSPDrawSurfaces;
- bspDrawSurfaces = safe_malloc_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
-
- memset( bspDrawSurfaces, 0, n * sizeof( bspDrawSurface_t ) );
+ bspDrawSurfaces = safe_malloc0_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
}
void BSPFilesCleanup(){
void AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length ){
bspLump_t *lump;
-
/* add lump to bsp file header */
lump = &header->lumps[ lumpNum ];
lump->offset = LittleLong( ftell( file ) );
lump->length = LittleLong( length );
/* write lump to file */
- SafeWrite( file, data, ( length + 3 ) & ~3 );
+ SafeWrite( file, data, length );
+
+ /* write padding zeros */
+ SafeWrite( file, (const byte[3]){ 0, 0, 0 }, ( ( length + 3 ) & ~3 ) - length );
}
PartialLoadIBSPFile( filename );
/* PartialSwapBSPFile() */
- int i, j;
- shaderInfo_t *si;
+ int i;
/* shaders (don't swap the name) */
for ( i = 0; i < numBSPShaders ; i++ )
/* allocate and clear new epair */
- e = safe_malloc( sizeof( epair_t ) );
- memset( e, 0, sizeof( epair_t ) );
+ e = safe_malloc0( sizeof( epair_t ) );
/* handle key */
if ( strlen( token ) >= ( MAX_KEY - 1 ) ) {
for ( i = beginArgs; i < endArgs; ++i )
{
+ if ( argv[i] == NULL ) {
+ continue;
+ }
if ( outpos != sentinel && i != beginArgs ) {
*outpos++ = ' ';
}
break;
}
if ( strcmp( token, "{" ) ) {
- Error( "ParseShaderFile: %s, line %d: { not found!\nFound instead: %s",
- temp, scriptline, token );
+ Error( "ParseShaderFile: %s, line %d: { not found!\nFound instead: %s\nFile location be: %s",
+ temp, scriptline, token, g_strLoadedFileLocation );
}
while ( 1 )
break;
}
if ( strcmp( token, "{" ) ) {
- Error( "ParseShaderFile: %s, line %d: { not found!\nFound instead: %s",
- temp, scriptline, token );
+ Error( "ParseShaderFile: %s, line %d: { not found!\nFound instead: %s\nFile location be: %s",
+ temp, scriptline, token, g_strLoadedFileLocation );
}
qboolean hasmap = qfalse;
break;
}
if ( strcmp( token, "{" ) ) {
- Error( "ParseShaderFile: %s, line %d: { not found!\nFound instead: %s",
- temp, scriptline, token );
+ Error( "ParseShaderFile: %s, line %d: { not found!\nFound instead: %s\nFile location be: %s",
+ temp, scriptline, token, g_strLoadedFileLocation );
}
strcat( shaderText, "\n{" );
qboolean hasmap = qfalse;
/* -help */
if ( !strcmp( argv[ i ], "-h" ) || !strcmp( argv[ i ], "--help" )
|| !strcmp( argv[ i ], "-help" ) ) {
- HelpMain(argv[i+1]);
+ HelpMain( ( i + 1 < argc ) ? argv[ i + 1 ] : NULL );
return 0;
}
/* -connect */
if ( !strcmp( argv[ i ], "-connect" ) ) {
- argv[ i ] = NULL;
- i++;
+ if ( ++i >= argc || !argv[ i ] ) {
+ Error( "Out of arguments: No address specified after %s", argv[ i - 1 ] );
+ }
+ argv[ i - 1 ] = NULL;
Broadcast_Setup( argv[ i ] );
argv[ i ] = NULL;
}
/* patch subdivisions */
else if ( !strcmp( argv[ i ], "-subdivisions" ) ) {
- argv[ i ] = NULL;
- i++;
+ if ( ++i >= argc || !argv[ i ] ) {
+ Error( "Out of arguments: No value specified after %s", argv[ i - 1 ] );
+ }
+ argv[ i - 1 ] = NULL;
patchSubdivisions = atoi( argv[ i ] );
argv[ i ] = NULL;
if ( patchSubdivisions <= 0 ) {
/* threads */
else if ( !strcmp( argv[ i ], "-threads" ) ) {
- argv[ i ] = NULL;
- i++;
+ if ( ++i >= argc || !argv[ i ] ) {
+ Error( "Out of arguments: No value specified after %s", argv[ i - 1 ] );
+ }
+ argv[ i - 1 ] = NULL;
numthreads = atoi( argv[ i ] );
argv[ i ] = NULL;
}
Sys_Printf( "Q3Map - v1.0r (c) 1999 Id Software Inc.\n" );
Sys_Printf( "Q3Map (ydnar) - v" Q3MAP_VERSION "\n" );
- Sys_Printf( "NetRadiant - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
+ Sys_Printf( RADIANT_NAME " - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
Sys_Printf( "%s\n", Q3MAP_MOTD );
Sys_Printf( "%s\n", argv[0] );
/* ydnar: otherwise create a bsp */
else{
+ /* used to write Smokin'Guns like tex file */
+ compile_map = qtrue;
+
r = BSPMain( argc, argv );
}
if ( !GetTokenAppend( buffer, qtrue ) || strcmp( token, "(" ) ) {
- Error( "Parse1DMatrixAppend(): line %d: ( not found!", scriptline );
+ Error( "Parse1DMatrixAppend(): line %d: ( not found!\nFile location be: %s\n", scriptline, g_strLoadedFileLocation );
}
for ( i = 0; i < x; i++ )
{
if ( !GetTokenAppend( buffer, qfalse ) ) {
- Error( "Parse1DMatrixAppend(): line %d: Number not found!", scriptline );
+ Error( "Parse1DMatrixAppend(): line %d: Number not found!\nFile location be: %s\n", scriptline, g_strLoadedFileLocation );
}
m[ i ] = atof( token );
}
if ( !GetTokenAppend( buffer, qtrue ) || strcmp( token, ")" ) ) {
- Error( "Parse1DMatrixAppend(): line %d: ) not found!", scriptline );
+ Error( "Parse1DMatrixAppend(): line %d: ) not found!\nFile location be: %s\n", scriptline, g_strLoadedFileLocation );
}
}
}
if ( strcmp( token, "{" ) ) {
if ( si != NULL ) {
- Error( "ParseShaderFile(): %s, line %d: { not found!\nFound instead: %s\nLast known shader: %s",
- filename, scriptline, token, si->shader );
+ Error( "ParseShaderFile(): %s, line %d: { not found!\nFound instead: %s\nLast known shader: %s\nFile location be: %s\n",
+ filename, scriptline, token, si->shader, g_strLoadedFileLocation );
}
else{
- Error( "ParseShaderFile(): %s, line %d: { not found!\nFound instead: %s",
- filename, scriptline, token );
+ Error( "ParseShaderFile(): %s, line %d: { not found!\nFound instead: %s\nFile location be: %s\n",
+ filename, scriptline, token, g_strLoadedFileLocation );
}
}
else if ( !Q_stricmp( token, "sun" ) /* sof2 */ || !Q_stricmp( token, "q3map_sun" ) || !Q_stricmp( token, "q3map_sunExt" ) ) {
float a, b;
sun_t *sun;
- qboolean ext;
-
+ qboolean ext = qfalse;
/* ydnar: extended sun directive? */
if ( !Q_stricmp( token, "q3map_sunext" ) ) {
}
/* allocate sun */
- sun = safe_malloc( sizeof( *sun ) );
- memset( sun, 0, sizeof( *sun ) );
+ sun = safe_malloc0( sizeof( *sun ) );
/* set style */
sun->style = si->lightStyle;
surfaceModel_t *model;
/* allocate new model and attach it */
- model = safe_malloc( sizeof( *model ) );
- memset( model, 0, sizeof( *model ) );
+ model = safe_malloc0( sizeof( *model ) );
model->next = si->surfaceModel;
si->surfaceModel = model;
/* allocate new foliage struct and attach it */
- foliage = safe_malloc( sizeof( *foliage ) );
- memset( foliage, 0, sizeof( *foliage ) );
+ foliage = safe_malloc0( sizeof( *foliage ) );
foliage->next = si->foliage;
si->foliage = foliage;
alpha = ( !Q_stricmp( token, "q3map_alphaGen" ) || !Q_stricmp( token, "q3map_alphaMod" ) ) ? 1 : 0;
/* allocate new colormod */
- cm = safe_malloc( sizeof( *cm ) );
- memset( cm, 0, sizeof( *cm ) );
+ cm = safe_malloc0( sizeof( *cm ) );
/* attach to shader */
if ( si->colorMod == NULL ) {