summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Nazarov <skuller@skuller.net>2010-07-03 16:01:48 +0000
committerAndrey Nazarov <skuller@skuller.net>2010-07-03 16:01:48 +0000
commit7a914ef872a436adcdb36d21ceddba6bb24eb993 (patch)
treedafc86024742e7bbced8ef897754beff5160f1ae
parent1acd69e9a128e4f3abf06b5ae97c968c87663864 (diff)
Yet another huge commit.
Implemented new error reporting framework based on errno and custom error codes. Converted filesystem code, image and model managers, BSP loader to use the new framework and report errors more accurately. Replaced fileHandle_t with qhandle_t. Removed INVALID_LENGTH definition. Killed USE_LOADBUF code. Defined FS_FileExists(Ex) macro, implemented FS_EasyOpenFile and FS_WriteFile helper functions. When testing for free screenshot slots, open the file with O_EXCL flag to avoid race conditions (works only on glibc yet). Don't allow quake paths with high bits set. Don't tolerate any fatal errors encountered when searching for files and pass errors back to the caller. FS_Seek() now fails on files from packs instead of returning an invalid position. Fixed ‘mvdskip’ command not working properly. Avoid inefficient usage of MakeColor macro in image loading functions. Prevent memory leak in JPEG and PNG image functions after calling longjmp() by marking some local variables volatile. Added support for loading monochrome JPEG images. Fixed signed integer overflow in GetWavinfo(). Fixed missing return statement in BSP_LoadSurfEdges() causing out of range edge indices to be left uncaught. Fixed possible access to uninitialized memory in BSP_LoadLeafs() for maps with no leafs. Properly NUL terminate the buffer in SCR_ScoreDump_f(), also check for being in a level. Always free latched_string when cvar is set back to current value. Fixed a crash in ‘mvdplay’ command possible when demo file is invalid.
-rw-r--r--build/common.mk3
-rw-r--r--source/bsp.c399
-rw-r--r--source/bsp.h2
-rw-r--r--source/cl_aastat.c39
-rw-r--r--source/cl_console.c44
-rw-r--r--source/cl_demo.c220
-rw-r--r--source/cl_http.c17
-rw-r--r--source/cl_keys.c2
-rw-r--r--source/cl_local.h50
-rw-r--r--source/cl_locs.c8
-rw-r--r--source/cl_main.c55
-rw-r--r--source/cl_parse.c42
-rw-r--r--source/cl_public.h2
-rw-r--r--source/cmd.c6
-rw-r--r--source/cmodel.c10
-rw-r--r--source/cmodel.h6
-rw-r--r--source/com_local.h8
-rw-r--r--source/common.c23
-rw-r--r--source/cvar.c22
-rw-r--r--source/error.c72
-rw-r--r--source/error.h65
-rw-r--r--source/files.c899
-rw-r--r--source/files.h98
-rw-r--r--source/gl_images.c29
-rw-r--r--source/gl_main.c129
-rw-r--r--source/gl_models.c137
-rw-r--r--source/gl_sky.c7
-rw-r--r--source/gl_surf.c6
-rw-r--r--source/in_evdev.c45
-rw-r--r--source/io_sleep.c1
-rw-r--r--source/mvd_client.c179
-rw-r--r--source/mvd_game.c8
-rw-r--r--source/mvd_local.h4
-rw-r--r--source/mvd_parse.c6
-rw-r--r--source/net_common.c22
-rw-r--r--source/prompt.c6
-rw-r--r--source/q_shared.h6
-rw-r--r--source/r_images.c1095
-rw-r--r--source/r_models.c222
-rw-r--r--source/r_models.h10
-rw-r--r--source/r_shared.h26
-rw-r--r--source/ref_public.h2
-rw-r--r--source/snd_mem.c68
-rw-r--r--source/snd_oss.c1
-rw-r--r--source/sv_ac.c11
-rw-r--r--source/sv_ccmds.c11
-rw-r--r--source/sv_init.c6
-rw-r--r--source/sv_mvd.c53
-rw-r--r--source/sv_save.c21
-rw-r--r--source/sv_user.c78
-rw-r--r--source/sw_image.c47
-rw-r--r--source/sw_model.c2
-rw-r--r--source/sys_public.h6
-rw-r--r--source/sys_unix.c17
-rw-r--r--source/ui_demos.c2
-rw-r--r--source/ui_playermodels.c2
-rw-r--r--source/ui_script.c5
57 files changed, 2270 insertions, 2092 deletions
diff --git a/build/common.mk b/build/common.mk
index f73df83..09a1587 100644
--- a/build/common.mk
+++ b/build/common.mk
@@ -30,7 +30,8 @@ SRCFILES+=cmd.c \
q_msg.c \
q_shared.c \
q_field.c \
- io_sleep.c
+ io_sleep.c \
+ error.c
ifdef USE_ZLIB
LDFLAGS+=$(ZLIB_LDFLAGS)
diff --git a/source/bsp.c b/source/bsp.c
index 493671f..755d3bf 100644
--- a/source/bsp.c
+++ b/source/bsp.c
@@ -27,8 +27,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "sys_public.h"
#include "bsp.h"
-void BSP_SetError( const char *fmt, ... ) q_printf( 1, 2 );
-
extern mtexinfo_t nulltexinfo;
static cvar_t *map_override_path;
@@ -41,32 +39,31 @@ static cvar_t *map_override_path;
===============================================================================
*/
-#define BSP_Malloc( size ) Hunk_Alloc( &bsp->pool, size )
+#define ALLOC( size ) \
+ Hunk_Alloc( &bsp->pool, size )
#define LOAD( Func ) \
- static qboolean BSP_Load##Func( bsp_t *bsp, void *base, size_t count )
+ static qerror_t BSP_Load##Func( bsp_t *bsp, void *base, size_t count )
+
+#define DEBUG( msg ) \
+ Com_DPrintf( "%s: %s\n", __func__, msg )
-/*
-=================
-Visibility
-=================
-*/
LOAD( Visibility ) {
unsigned numclusters, bitofs;
int i, j;
if( !count ) {
- return qtrue;
+ return Q_ERR_SUCCESS;
}
bsp->numvisibility = count;
- bsp->vis = BSP_Malloc( count );
+ bsp->vis = ALLOC( count );
memcpy( bsp->vis, base, count );
numclusters = LittleLong( bsp->vis->numclusters );
if( numclusters > ( count - 4 ) / 8 ) {
- BSP_SetError( "%s: bad numclusters", __func__ );
- return qfalse;
+ DEBUG( "bad numclusters" );
+ return Q_ERR_TOO_MANY;
}
bsp->vis->numclusters = numclusters;
bsp->visrowsize = ( numclusters + 7 ) >> 3;
@@ -74,21 +71,16 @@ LOAD( Visibility ) {
for( j = 0; j < 2; j++ ) {
bitofs = LittleLong( bsp->vis->bitofs[i][j] );
if( bitofs >= count ) {
- BSP_SetError( "%s: bad bitofs", __func__ );
- return qfalse;
+ DEBUG( "bad bitofs" );
+ return Q_ERR_BAD_INDEX;
}
bsp->vis->bitofs[i][j] = bitofs;
}
}
- return qtrue;
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-Texinfo
-=================
-*/
LOAD( Texinfo ) {
dtexinfo_t *in;
mtexinfo_t *out;
@@ -100,7 +92,7 @@ LOAD( Texinfo ) {
#endif
bsp->numtexinfo = count;
- bsp->texinfo = BSP_Malloc( sizeof( *out ) * count );
+ bsp->texinfo = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->texinfo;
@@ -123,8 +115,8 @@ LOAD( Texinfo ) {
next = LittleLong( in->nexttexinfo );
if( next > 0 ) {
if( next >= count ) {
- BSP_SetError( "%s: bad anim chain", __func__ );
- return qfalse;
+ DEBUG( "bad anim chain" );
+ return Q_ERR_BAD_INDEX;
}
out->next = bsp->texinfo + next;
} else {
@@ -143,21 +135,17 @@ LOAD( Texinfo ) {
}
}
#endif
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-Planes
-=================
-*/
LOAD( Planes ) {
dplane_t *in;
cplane_t *out;
int i, j;
bsp->numplanes = count;
- bsp->planes = BSP_Malloc( sizeof( *out ) * count );
+ bsp->planes = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->planes;
@@ -169,14 +157,10 @@ LOAD( Planes ) {
SetPlaneType( out );
SetPlaneSignbits( out );
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-BrushSides
-=================
-*/
LOAD( BrushSides ) {
dbrushside_t *in;
mbrushside_t *out;
@@ -184,15 +168,15 @@ LOAD( BrushSides ) {
unsigned planenum, texinfo;
bsp->numbrushsides = count;
- bsp->brushsides = BSP_Malloc( sizeof( *out ) * count );
+ bsp->brushsides = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->brushsides;
for( i = 0; i < count; i++, in++, out++ ) {
planenum = LittleShort (in->planenum);
if( planenum >= bsp->numplanes ) {
- BSP_SetError( "%s: bad planenum", __func__ );
- return qfalse;
+ DEBUG( "bad planenum" );
+ return Q_ERR_BAD_INDEX;
}
out->plane = bsp->planes + planenum;
texinfo = LittleShort (in->texinfo);
@@ -200,20 +184,16 @@ LOAD( BrushSides ) {
out->texinfo = &nulltexinfo;
} else {
if (texinfo >= bsp->numtexinfo) {
- BSP_SetError( "%s: bad texinfo", __func__ );
- return qfalse;
+ DEBUG( "bad texinfo" );
+ return Q_ERR_BAD_INDEX;
}
out->texinfo = bsp->texinfo + texinfo;
}
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-Brushes
-=================
-*/
LOAD( Brushes ) {
dbrush_t *in;
mbrush_t *out;
@@ -221,7 +201,7 @@ LOAD( Brushes ) {
unsigned firstside, numsides, lastside;
bsp->numbrushes = count;
- bsp->brushes = BSP_Malloc( sizeof( *out ) * count );
+ bsp->brushes = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->brushes;
@@ -230,22 +210,18 @@ LOAD( Brushes ) {
numsides = LittleLong(in->numsides);
lastside = firstside + numsides;
if( lastside < firstside || lastside > bsp->numbrushsides ) {
- BSP_SetError( "%s: bad brushsides", __func__ );
- return qfalse;
+ DEBUG( "bad brushsides" );
+ return Q_ERR_BAD_INDEX;
}
out->firstbrushside = bsp->brushsides + firstside;
out->numsides = numsides;
out->contents = LittleLong(in->contents);
out->checkcount = 0;
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-LeafBrushes
-=================
-*/
LOAD( LeafBrushes ) {
uint16_t *in;
mbrush_t **out;
@@ -253,28 +229,24 @@ LOAD( LeafBrushes ) {
unsigned brushnum;
bsp->numleafbrushes = count;
- bsp->leafbrushes = BSP_Malloc( sizeof( *out ) * count );
+ bsp->leafbrushes = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->leafbrushes;
for( i = 0; i < count; i++, in++, out++ ) {
brushnum = LittleShort( *in );
if( brushnum >= bsp->numbrushes ) {
- BSP_SetError( "%s: bad brushnum", __func__ );
- return qfalse;
+ DEBUG( "bad brushnum" );
+ return Q_ERR_BAD_INDEX;
}
*out = bsp->brushes + brushnum;
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
#if USE_REF
-/*
-=================
-Lightmap
-=================
-*/
LOAD( Lightmap ) {
#if USE_REF == REF_SOFT
byte *in;
@@ -285,11 +257,11 @@ LOAD( Lightmap ) {
#endif
if( !count ) {
- return qtrue;
+ return Q_ERR_SUCCESS;
}
bsp->numlightmapbytes = count;
- bsp->lightmap = BSP_Malloc( count );
+ bsp->lightmap = ALLOC( count );
#if USE_REF == REF_SOFT
// convert the 24 bit lighting down to 8 bit
@@ -307,21 +279,17 @@ LOAD( Lightmap ) {
#else
memcpy( bsp->lightmap, base, count );
#endif
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-Vertices
-=================
-*/
LOAD( Vertices ) {
dvertex_t *in;
mvertex_t *out;
int i, j;
bsp->numvertices = count;
- bsp->vertices = BSP_Malloc( sizeof( *out ) * count );
+ bsp->vertices = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->vertices;
@@ -330,14 +298,10 @@ LOAD( Vertices ) {
out->point[j] = LittleFloat( in->point[j] );
}
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-Edges
-=================
-*/
LOAD( Edges ) {
dedge_t *in;
medge_t *out;
@@ -345,7 +309,7 @@ LOAD( Edges ) {
unsigned vertnum;
bsp->numedges = count;
- bsp->edges = BSP_Malloc( sizeof( *out ) * count );
+ bsp->edges = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->edges;
@@ -353,20 +317,16 @@ LOAD( Edges ) {
for( j = 0; j < 2; j++ ) {
vertnum = LittleShort( in->v[j] );
if( vertnum >= bsp->numvertices ) {
- BSP_SetError( "%s: bad vertnum", __func__ );
- return qfalse;
+ DEBUG( "bad vertnum" );
+ return Q_ERR_BAD_INDEX;
}
out->v[j] = bsp->vertices + vertnum;
}
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-SurfEdges
-=================
-*/
LOAD( SurfEdges ) {
int *in;
msurfedge_t *out;
@@ -374,7 +334,7 @@ LOAD( SurfEdges ) {
int index, vert;
bsp->numsurfedges = count;
- bsp->surfedges = BSP_Malloc( sizeof( *out ) * count );
+ bsp->surfedges = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->surfedges;
@@ -388,20 +348,17 @@ LOAD( SurfEdges ) {
}
if( index >= bsp->numedges ) {
- BSP_SetError( "%s: bad edgenum", __func__ );
+ DEBUG( "bad edgenum" );
+ return Q_ERR_BAD_INDEX;
}
out->edge = bsp->edges + index;
out->vert = vert;
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-Faces
-=================
-*/
LOAD( Faces ) {
dface_t *in;
mface_t *out;
@@ -414,7 +371,7 @@ LOAD( Faces ) {
unsigned planenum, side;
bsp->numfaces = count;
- bsp->faces = BSP_Malloc( sizeof( *out ) * count );
+ bsp->faces = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->faces;
@@ -422,24 +379,28 @@ LOAD( Faces ) {
firstedge = LittleLong( in->firstedge );
numedges = LittleShort( in->numedges );
lastedge = firstedge + numedges;
- if( numedges < 3 || lastedge < firstedge || lastedge > bsp->numsurfedges ) {
- BSP_SetError( "%s: bad surfedges", __func__ );
- return qfalse;
+ if( numedges < 3 ) {
+ DEBUG( "bad surfedges" );
+ return Q_ERR_TOO_FEW;
+ }
+ if( lastedge < firstedge || lastedge > bsp->numsurfedges ) {
+ DEBUG( "bad surfedges" );
+ return Q_ERR_BAD_INDEX;
}
out->firstsurfedge = bsp->surfedges + firstedge;
out->numsurfedges = numedges;
planenum = LittleShort( in->planenum );
if( planenum >= bsp->numplanes ) {
- BSP_SetError( "%s: bad planenum", __func__ );
- return qfalse;
+ DEBUG( "bad planenum" );
+ return Q_ERR_BAD_INDEX;
}
out->plane = bsp->planes + planenum;
texinfo = LittleShort( in->texinfo );
if( texinfo >= bsp->numtexinfo ) {
- BSP_SetError( "%s: bad texinfo", __func__ );
- return qfalse;
+ DEBUG( "bad texinfo" );
+ return Q_ERR_BAD_INDEX;
}
out->texinfo = bsp->texinfo + texinfo;
@@ -458,8 +419,8 @@ LOAD( Faces ) {
lightofs /= 3;
#endif
if( lightofs >= bsp->numlightmapbytes ) {
- BSP_SetError( "%s: bad lightofs", __func__ );
- return qfalse;
+ DEBUG( "bad lightofs" );
+ return Q_ERR_BAD_INDEX;
}
out->lightmap = bsp->lightmap + lightofs;
}
@@ -467,14 +428,10 @@ LOAD( Faces ) {
side = LittleShort( in->side );
out->drawflags = side & DSURF_PLANEBACK;
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-LeafFaces
-=================
-*/
LOAD( LeafFaces ) {
uint16_t *in;
mface_t **out;
@@ -482,27 +439,23 @@ LOAD( LeafFaces ) {
unsigned facenum;
bsp->numleaffaces = count;
- bsp->leaffaces = BSP_Malloc( sizeof( *out ) * count );
+ bsp->leaffaces = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->leaffaces;
for( i = 0; i < count; i++, in++, out++ ) {
facenum = LittleShort( *in );
if( facenum >= bsp->numfaces ) {
- BSP_SetError( "%s: bad facenum", __func__ );
- return qfalse;
+ DEBUG( "bad facenum" );
+ return Q_ERR_BAD_INDEX;
}
*out = bsp->faces + facenum;
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
#endif
-/*
-=================
-Leafs
-=================
-*/
LOAD( Leafs ) {
dleaf_t *in;
mleaf_t *out;
@@ -513,8 +466,13 @@ LOAD( Leafs ) {
unsigned firstleafface, numleaffaces;
#endif
+ if( !count ) {
+ DEBUG( "map with no leafs" );
+ return Q_ERR_TOO_FEW;
+ }
+
bsp->numleafs = count;
- bsp->leafs = BSP_Malloc( sizeof( *out ) * count );
+ bsp->leafs = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->leafs;
@@ -524,24 +482,24 @@ LOAD( Leafs ) {
cluster = ( signed short )LittleShort (in->cluster);
if( bsp->vis && cluster != -1 ) {
if( cluster < 0 || cluster >= bsp->vis->numclusters ) {
- BSP_SetError( "%s: bad cluster", __func__ );
- return qfalse;
+ DEBUG( "bad cluster" );
+ return Q_ERR_BAD_INDEX;
}
}
out->cluster = cluster;
area = LittleShort( in->area );
if( area >= bsp->numareas ) {
- BSP_SetError( "%s: bad area", __func__ );
- return qfalse;
+ DEBUG( "bad area" );
+ return Q_ERR_BAD_INDEX;
}
out->area = area;
firstleafbrush = LittleShort (in->firstleafbrush);
numleafbrushes = LittleShort (in->numleafbrushes);
if( firstleafbrush + numleafbrushes > bsp->numleafbrushes ) {
- BSP_SetError( "%s: bad leafbrushes", __func__ );
- return qfalse;
+ DEBUG( "bad leafbrushes" );
+ return Q_ERR_BAD_INDEX;
}
out->firstleafbrush = bsp->leafbrushes + firstleafbrush;
out->numleafbrushes = numleafbrushes;
@@ -550,8 +508,8 @@ LOAD( Leafs ) {
firstleafface = LittleShort (in->firstleafface);
numleaffaces = LittleShort (in->numleaffaces);
if( firstleafface + numleaffaces > bsp->numleaffaces ) {
- BSP_SetError( "%s: bad leaffaces", __func__ );
- return qfalse;
+ DEBUG( "bad leaffaces" );
+ return Q_ERR_BAD_INDEX;
}
out->firstleafface = bsp->leaffaces + firstleafface;
out->numleaffaces = numleaffaces;
@@ -567,17 +525,13 @@ LOAD( Leafs ) {
}
if (bsp->leafs[0].contents != CONTENTS_SOLID) {
- BSP_SetError( "%s: map leaf 0 is not CONTENTS_SOLID", __func__ );
- return qfalse;
+ DEBUG( "map leaf 0 is not CONTENTS_SOLID" );
+ return Q_ERR_INVALID_FORMAT;
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-Nodes
-=================
-*/
LOAD( Nodes ) {
dnode_t *in;
uint32_t child;
@@ -585,24 +539,24 @@ LOAD( Nodes ) {
int i, j;
unsigned planeNum;
#if USE_REF
- unsigned firstface, numfaces, lastface;
+ unsigned firstface, numfaces;
#endif
if( !count ) {
- BSP_SetError( "%s: map with no nodes", __func__ );
- return qfalse;
+ DEBUG( "map with no nodes" );
+ return Q_ERR_TOO_FEW;
}
bsp->numnodes = count;
- bsp->nodes = BSP_Malloc( sizeof( *out ) * count );
+ bsp->nodes = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->nodes;
for( i = 0; i < count; i++, out++, in++ ) {
planeNum = LittleLong(in->planenum);
if( planeNum >= bsp->numplanes ) {
- BSP_SetError( "%s: bad planenum", __func__ );
- return qfalse;
+ DEBUG( "bad planenum" );
+ return Q_ERR_BAD_INDEX;
}
out->plane = bsp->planes + planeNum;
@@ -611,14 +565,14 @@ LOAD( Nodes ) {
if( child & 0x80000000 ) {
child = ~child;
if( child >= bsp->numleafs ) {
- BSP_SetError( "%s: bad leafnum", __func__ );
- return qfalse;
+ DEBUG( "bad leafnum" );
+ return Q_ERR_BAD_INDEX;
}
out->children[j] = ( mnode_t * )( bsp->leafs + child );
} else {
if( child >= count ) {
- BSP_SetError( "%s: bad nodenum", __func__ );
- return qfalse;
+ DEBUG( "bad nodenum" );
+ return Q_ERR_BAD_INDEX;
}
out->children[j] = bsp->nodes + child;
}
@@ -627,10 +581,9 @@ LOAD( Nodes ) {
#if USE_REF
firstface = LittleShort( in->firstface );
numfaces = LittleShort( in->numfaces );
- lastface = firstface + numfaces;
- if( lastface < firstface || lastface > bsp->numfaces ) {
- BSP_SetError( "%s: bad faces", __func__ );
- return qfalse;
+ if( firstface + numfaces > bsp->numfaces ) {
+ DEBUG( "bad faces" );
+ return Q_ERR_BAD_INDEX;
}
out->firstface = bsp->faces + firstface;
out->numfaces = numfaces;
@@ -644,14 +597,10 @@ LOAD( Nodes ) {
out->visframe = -1;
#endif
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-Submodels
-=================
-*/
LOAD( Submodels ) {
dmodel_t *in;
mmodel_t *out;
@@ -661,7 +610,7 @@ LOAD( Submodels ) {
unsigned firstface, numfaces, lastface;
#endif
- bsp->models = BSP_Malloc( sizeof( *out ) * count );
+ bsp->models = ALLOC( sizeof( *out ) * count );
bsp->nummodels = count;
in = base;
@@ -686,8 +635,8 @@ LOAD( Submodels ) {
numfaces = LittleLong( in->numfaces );
lastface = firstface + numfaces;
if( lastface < firstface || lastface > bsp->numfaces ) {
- BSP_SetError( "%s: bad faces", __func__ );
- return qfalse;
+ DEBUG( "bad faces" );
+ return Q_ERR_BAD_INDEX;
}
out->firstface = bsp->faces + firstface;
out->numfaces = numfaces;
@@ -695,23 +644,18 @@ LOAD( Submodels ) {
out->radius = RadiusFromBounds( out->mins, out->maxs );
#endif
}
- return qtrue;
-}
-/*
-=================
-AreaPortals
+ return Q_ERR_SUCCESS;
+}
-These are validated after all the areas are loaded
-=================
-*/
+// These are validated after all the areas are loaded
LOAD( AreaPortals ) {
dareaportal_t *in;
mareaportal_t *out;
int i;
bsp->numareaportals = count;
- bsp->areaportals = BSP_Malloc( sizeof( *out ) * count );
+ bsp->areaportals = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->areaportals;
@@ -719,14 +663,10 @@ LOAD( AreaPortals ) {
out->portalnum = LittleLong (in->portalnum);
out->otherarea = LittleLong (in->otherarea);
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-Areas
-=================
-*/
LOAD( Areas ) {
darea_t *in;
marea_t *out;
@@ -734,7 +674,7 @@ LOAD( Areas ) {
unsigned numareaportals, firstareaportal, lastareaportal;
bsp->numareas = count;
- bsp->areas = BSP_Malloc( sizeof( *out ) * count );
+ bsp->areas = ALLOC( sizeof( *out ) * count );
in = base;
out = bsp->areas;
@@ -743,21 +683,17 @@ LOAD( Areas ) {
firstareaportal = LittleLong (in->firstareaportal);
lastareaportal = firstareaportal + numareaportals;
if( lastareaportal < firstareaportal || lastareaportal > bsp->numareaportals ) {
- BSP_SetError( "%s: bad areaportals", __func__ );
- return qfalse;
+ DEBUG( "bad areaportals" );
+ return Q_ERR_BAD_INDEX;
}
out->numareaportals = numareaportals;
out->firstareaportal = bsp->areaportals + firstareaportal;
out->floodvalid = 0;
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
-/*
-=================
-EntityString
-=================
-*/
LOAD( EntityString ) {
char *path = map_override_path->string;
@@ -773,20 +709,21 @@ LOAD( EntityString ) {
len = FS_LoadFile( buffer, ( void ** )&str );
if( str ) {
Com_DPrintf( "Loaded entity string from %s\n", buffer );
- bsp->entitystring = BSP_Malloc( len + 1 );
+ bsp->entitystring = ALLOC( len + 1 );
memcpy( bsp->entitystring, str, len + 1 );
bsp->numentitychars = len;
FS_FreeFile( str );
return qtrue;
}
- Com_DPrintf( "Couldn't load entity string from %s\n", buffer );
+ Com_DPrintf( "Couldn't load entity string from %s: %s\n", buffer, Q_ErrorString( len ) );
}
bsp->numentitychars = count;
- bsp->entitystring = BSP_Malloc( count + 1 );
+ bsp->entitystring = ALLOC( count + 1 );
memcpy( bsp->entitystring, base, count );
bsp->entitystring[count] = 0;
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
@@ -799,7 +736,7 @@ LOAD( EntityString ) {
*/
typedef struct {
- qboolean (*load)( bsp_t *, void *, size_t );
+ qerror_t (*load)( bsp_t *, void *, size_t );
int lump;
size_t size;
size_t maxcount;
@@ -833,7 +770,6 @@ static const lump_info_t bsp_lumps[] = {
};
static list_t bsp_cache;
-static char bsp_error[MAX_QPATH];
static void BSP_List_f( void ) {
bsp_t *bsp;
@@ -863,65 +799,56 @@ static bsp_t *BSP_Find( const char *name ) {
return bsp;
}
}
+
return NULL;
}
-static qboolean BSP_SetParent( mnode_t *node ) {
+static qerror_t BSP_SetParent( mnode_t *node ) {
mnode_t *child;
while( node->plane ) {
child = node->children[0];
if( child->parent ) {
- return qfalse;
+ DEBUG( "cycle encountered" );
+ return Q_ERR_DEADLOCK;
}
child->parent = node;
BSP_SetParent( child );
child = node->children[1];
if( child->parent ) {
- return qfalse;
+ DEBUG( "cycle encountered" );
+ return Q_ERR_DEADLOCK;
}
child->parent = node;
node = child;
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
// also calculates the last portal number used
// by CM code to allocate portalopen[] array
-static qboolean BSP_ValidateAreaPortals( bsp_t *bsp ) {
+static qerror_t BSP_ValidateAreaPortals( bsp_t *bsp ) {
mareaportal_t *p;
int i;
bsp->lastareaportal = 0;
for( i = 0, p = bsp->areaportals; i < bsp->numareaportals; i++, p++ ) {
if( p->portalnum >= MAX_MAP_AREAPORTALS ) {
- BSP_SetError( "%s: bad portalnum", __func__ );
- return qfalse;
+ DEBUG( "bad portalnum" );
+ return Q_ERR_TOO_MANY;
}
if( p->portalnum > bsp->lastareaportal ) {
bsp->lastareaportal = p->portalnum;
}
if( p->otherarea >= bsp->numareas ) {
- BSP_SetError( "%s: bad otherarea", __func__ );
- return qfalse;
+ DEBUG( "bad otherarea" );
+ return Q_ERR_BAD_INDEX;
}
}
- return qtrue;
-}
-
-
-void BSP_SetError( const char *fmt, ... ) {
- va_list argptr;
-
- va_start( argptr, fmt );
- Q_vsnprintf( bsp_error, sizeof( bsp_error ), fmt, argptr );
- va_end( argptr );
-}
-
-const char *BSP_GetError( void ) {
- return bsp_error;
+ return Q_ERR_SUCCESS;
}
void BSP_Free( bsp_t *bsp ) {
@@ -946,33 +873,33 @@ BSP_Load
Loads in the map and all submodels
==================
*/
-bsp_t *BSP_Load( const char *name ) {
+qerror_t BSP_Load( const char *name, bsp_t **bsp_p ) {
bsp_t *bsp;
byte *buf;
dheader_t *header;
const lump_info_t *info;
size_t filelen, ofs, len, end, count;
+ qerror_t ret;
if( !name || !name[0] ) {
Com_Error( ERR_FATAL, "%s: NULL", __func__ );
}
- BSP_SetError( "no error" );
-
if( ( bsp = BSP_Find( name ) ) != NULL ) {
Com_PageInMemory( bsp->pool.base, bsp->pool.cursize );
bsp->refcount++;
- return bsp;
+ *bsp_p = bsp;
+ return Q_ERR_SUCCESS;
}
+ *bsp_p = NULL;
//
// load the file
//
filelen = FS_LoadFile( name, ( void ** )&buf );
if( !buf ) {
- BSP_SetError( "file not found" );
- return NULL;
+ return filelen;
}
len = strlen( name );
@@ -983,11 +910,11 @@ bsp_t *BSP_Load( const char *name ) {
// byte swap and validate the header
header = ( dheader_t * )buf;
if( LittleLong( header->ident ) != IDBSPHEADER ) {
- BSP_SetError( "not an IBSP file" );
+ ret = Q_ERR_UNKNOWN_FORMAT;
goto fail2;
}
if( LittleLong( header->version ) != BSPVERSION ) {
- BSP_SetError( "unsupported IBSP version" );
+ ret = Q_ERR_UNKNOWN_FORMAT;
goto fail2;
}
@@ -1007,45 +934,49 @@ bsp_t *BSP_Load( const char *name ) {
len = LittleLong( header->lumps[info->lump].filelen );
end = ofs + len;
if( end < ofs || end > filelen ) {
- BSP_SetError( "lump %d extents are out of bounds", info->lump );
+ ret = Q_ERR_BAD_EXTENT;
goto fail1;
}
if( len % info->size ) {
- BSP_SetError( "lump %d has funny size", info->lump );
+ ret = Q_ERR_ODD_SIZE;
goto fail1;
}
count = len / info->size;
if( count > info->maxcount ) {
- BSP_SetError( "lump %d has too many elements", info->lump );
+ ret = Q_ERR_TOO_MANY;
goto fail1;
}
- if( !info->load( bsp, buf + ofs, count ) ) {
+ ret = info->load( bsp, buf + ofs, count );
+ if( ret ) {
goto fail1;
}
}
- if( !BSP_ValidateAreaPortals( bsp ) ) {
+ ret = BSP_ValidateAreaPortals( bsp );
+ if( ret ) {
goto fail1;
}
- if( !BSP_SetParent( bsp->nodes ) ) {
- BSP_SetError( "cycle encountered in BSP graph" );
+ ret = BSP_SetParent( bsp->nodes );
+ if( ret ) {
goto fail1;
}
Hunk_End( &bsp->pool );
+ List_Append( &bsp_cache, &bsp->entry );
+
FS_FreeFile( buf );
- List_Append( &bsp_cache, &bsp->entry );
- return bsp;
+ *bsp_p = bsp;
+ return Q_ERR_SUCCESS;
fail1:
Hunk_Free( &bsp->pool );
fail2:
FS_FreeFile( buf );
Z_Free( bsp );
- return NULL;
+ return ret;
}
/*
diff --git a/source/bsp.h b/source/bsp.h
index 868ab20..63a3d19 100644
--- a/source/bsp.h
+++ b/source/bsp.h
@@ -259,7 +259,7 @@ typedef struct bsp_s {
char name[1];
} bsp_t;
-bsp_t *BSP_Load( const char *name );
+qerror_t BSP_Load( const char *name, bsp_t **bsp_p );
void BSP_Free( bsp_t *bsp );
const char *BSP_GetError( void );
diff --git a/source/cl_aastat.c b/source/cl_aastat.c
index b1f305f..0b14549 100644
--- a/source/cl_aastat.c
+++ b/source/cl_aastat.c
@@ -292,9 +292,9 @@ static void TH_DrawLayoutString( char *dst, const char *s ) {
static void SCR_ScoreShot_f( void ) {
char buffer[( TH_WIDTH + 1 ) * TH_HEIGHT];
char path[MAX_OSPATH];
- fileHandle_t f;
- size_t len;
+ qhandle_t f;
int i;
+ qerror_t ret;
if( cls.state != ca_active ) {
Com_Printf( "Must be in a level.\n" );
@@ -302,31 +302,32 @@ static void SCR_ScoreShot_f( void ) {
}
if( Cmd_Argc() > 1 ) {
- len = Q_concat( path, sizeof( path ), SCORESHOTS_DIRECTORY "/", Cmd_Argv( 1 ), ".txt", NULL );
- if( len >= sizeof( path ) ) {
- Com_EPrintf( "Oversize filename specified.\n" );
+ f = FS_EasyOpenFile( path, sizeof( path ), FS_MODE_WRITE,
+ SCORESHOTS_DIRECTORY "/", Cmd_Argv( 1 ), ".txt" );
+ if( !f ) {
return;
}
} else {
+ // find a file name to save it to
for( i = 0; i < 1000; i++ ) {
Q_snprintf( path, sizeof( path ), SCORESHOTS_DIRECTORY "/quake%03d.txt", i );
- if( FS_LoadFileEx( path, NULL, FS_PATH_GAME, TAG_FREE ) == INVALID_LENGTH ) {
- break; // file doesn't exist
+ ret = FS_FOpenFile( path, &f, FS_MODE_WRITE|FS_FLAG_EXCL );
+ if( f ) {
+ break;
+ }
+ if( ret != Q_ERR_EXIST ) {
+ Com_EPrintf( "Couldn't exclusively open %s for writing: %s\n",
+ path, Q_ErrorString( ret ) );
+ return;
}
}
if( i == 1000 ) {
- Com_Printf( "All scoreshot slots are full.\n" );
+ Com_EPrintf( "All scoreshot slots are full.\n" );
return;
}
}
- FS_FOpenFile( path, &f, FS_MODE_WRITE );
- if( !f ) {
- Com_EPrintf( "Couldn't open %s for writing.\n", path );
- return;
- }
-
memset( buffer, ' ', sizeof( buffer ) );
for( i = 0; i < TH_HEIGHT; i++ ) {
buffer[ i * ( TH_WIDTH + 1 ) + TH_WIDTH ] = '\n';
@@ -346,15 +347,21 @@ static void SCR_ScoreDump_f( void ) {
char buffer[( TH_WIDTH + 1 ) * TH_HEIGHT];
int i;
+ if( cls.state != ca_active ) {
+ Com_Printf( "Must be in a level.\n" );
+ return;
+ }
+
memset( buffer, ' ', sizeof( buffer ) );
- for( i = 0; i < TH_HEIGHT; i++ ) {
+ for( i = 0; i < TH_HEIGHT - 1; i++ ) {
buffer[ i * ( TH_WIDTH + 1 ) + TH_WIDTH ] = '\n';
}
+ buffer[ i * ( TH_WIDTH + 1 ) + TH_WIDTH ] = 0;
TH_DrawLayoutString( buffer, cl.configstrings[CS_STATUSBAR] );
TH_DrawLayoutString( buffer, cl.layout );
- Com_Printf( "%s", buffer );
+ Com_Printf( "%s\n", buffer );
}
void CL_InitAscii( void ) {
diff --git a/source/cl_console.c b/source/cl_console.c
index c6ddcdb..efd7467 100644
--- a/source/cl_console.c
+++ b/source/cl_console.c
@@ -202,24 +202,17 @@ Save the console contents out to a file
static void Con_Dump_f( void ) {
int l;
char *line;
- fileHandle_t f;
+ qhandle_t f;
char name[MAX_OSPATH];
- size_t len;
if( Cmd_Argc() != 2 ) {
Com_Printf( "Usage: %s <filename>\n", Cmd_Argv( 0 ) );
return;
}
- len = Q_concat( name, sizeof( name ), "condumps/", Cmd_Argv( 1 ), ".txt", NULL );
- if( len >= sizeof( name ) ) {
- Com_EPrintf( "Oversize filename specified.\n" );
- return;
- }
-
- FS_FOpenFile( name, &f, FS_MODE_WRITE );
+ f = FS_EasyOpenFile( name, sizeof( name ), FS_MODE_WRITE,
+ "condumps/", Cmd_Argv( 1 ), ".txt" );
if( !f ) {
- Com_EPrintf( "Couldn't open %s for writing.\n", name );
return;
}
@@ -563,19 +556,30 @@ Con_RegisterMedia
================
*/
void Con_RegisterMedia( void ) {
- con.charsetImage = R_RegisterFont( con_font->string );
- if( !con.charsetImage && strcmp( con_font->string, "conchars" ) ) {
- Com_WPrintf( "Couldn't load console font: %s\n", con_font->string );
- con.charsetImage = R_RegisterFont( "conchars" );
- }
+ qerror_t ret;
+
+ ret = _R_RegisterFont( con_font->string, &con.charsetImage );
if( !con.charsetImage ) {
- Com_Error( ERR_FATAL, "Couldn't load pics/conchars.pcx" );
+ if( strcmp( con_font->string, "conchars" ) ) {
+ Com_WPrintf( "Couldn't load console font: %s\n", Q_ErrorString( ret ) );
+ Cvar_Reset( con_font );
+ ret = _R_RegisterFont( "conchars", &con.charsetImage );
+ }
+ if( !con.charsetImage ) {
+ Com_Error( ERR_FATAL, "Couldn't load pics/conchars.pcx: %s", Q_ErrorString( ret ) );
+ }
}
- con.backImage = R_RegisterPic( con_background->string );
- if( !con.backImage && strcmp( con_background->string, "conback" ) ) {
- Com_WPrintf( "Couldn't load console background: %s\n", con_background->string );
- con.backImage = R_RegisterFont( "conback" );
+ ret = _R_RegisterPic( con_background->string, &con.backImage );
+ if( !con.backImage ) {
+ if( strcmp( con_background->string, "conback" ) ) {
+ Com_WPrintf( "Couldn't load console background: %s\n", Q_ErrorString( ret ) );
+ Cvar_Reset( con_background );
+ ret = _R_RegisterPic( "conback", &con.backImage );
+ }
+ if( !con.charsetImage ) {
+ Com_EPrintf( "Couldn't load pics/conback.pcx: %s\n", Q_ErrorString( ret ) );
+ }
}
}
diff --git a/source/cl_demo.c b/source/cl_demo.c
index 1f33655..f8fc6f2 100644
--- a/source/cl_demo.c
+++ b/source/cl_demo.c
@@ -215,6 +215,7 @@ stop recording a demo
*/
void CL_Stop_f( void ) {
uint32_t msglen;
+ ssize_t pos;
if( !cls.demo.recording ) {
Com_Printf( "Not recording a demo.\n" );
@@ -234,17 +235,17 @@ void CL_Stop_f( void ) {
FS_Write( &msglen, 4, cls.demo.recording );
FS_Flush( cls.demo.recording );
- msglen = FS_Tell( cls.demo.recording );
+ pos = FS_Tell( cls.demo.recording );
// close demofile
FS_FCloseFile( cls.demo.recording );
cls.demo.recording = 0;
cls.demo.paused = qfalse;
- if( msglen == INVALID_LENGTH ) {
+ if( pos < 0 ) {
Com_Printf( "Stopped demo.\n" );
} else {
- Com_Printf( "Stopped demo (%u bytes written).\n", msglen );
+ Com_Printf( "Stopped demo (%d bytes written).\n", (int)pos );
}
}
@@ -263,8 +264,8 @@ static void CL_Record_f( void ) {
size_t len;
entity_state_t *ent;
char *string;
- fileHandle_t demofile;
- qboolean gzip = qfalse;
+ qhandle_t f;
+ unsigned mode = FS_MODE_WRITE;
if( cls.demo.recording ) {
Com_Printf( "Already recording.\n" );
@@ -284,7 +285,7 @@ static void CL_Record_f( void ) {
Cmd_PrintHelp( o_record );
return;
case 'z':
- gzip = qtrue;
+ mode |= FS_FLAG_GZIP;
break;
default:
return;
@@ -300,26 +301,15 @@ static void CL_Record_f( void ) {
//
// open the demo file
//
- len = Q_concat( name, sizeof( name ), "demos/", cmd_optarg,
- gzip ? ".dm2.gz" : ".dm2", NULL );
- if( len >= sizeof( name ) ) {
- Com_EPrintf( "Oversize filename specified.\n" );
- return;
- }
-
- FS_FOpenFile( name, &demofile, FS_MODE_WRITE );
- if( !demofile ) {
- Com_EPrintf( "Couldn't open %s for writing.\n", name );
+ f = FS_EasyOpenFile( name, sizeof( name ), mode,
+ "demos/", cmd_optarg, ".dm2" );
+ if( !f ) {
return;
}
Com_Printf( "Recording client demo to %s.\n", name );
- if( gzip ) {
- FS_FilterFile( demofile );
- }
-
- cls.demo.recording = demofile;
+ cls.demo.recording = f;
cls.demo.paused = qfalse;
SZ_Init( &cls.demo.buffer, demo_buffer, sizeof( demo_buffer ) );
@@ -446,118 +436,117 @@ static void CL_Suspend_f( void ) {
memset( cl.dcs, 0, sizeof( cl.dcs ) );
}
-static int CL_ReadFirstDemoMessage( fileHandle_t f ) {
+static int read_first_message( qhandle_t f ) {
uint32_t ul;
uint16_t us;
size_t msglen;
+ ssize_t read;
+ qerror_t ret;
int type;
// read magic/msglen
- if( FS_Read( &ul, 4, f ) != 4 ) {
- Com_DPrintf( "%s: short read of msglen\n", __func__ );
- return -1;
+ read = FS_Read( &ul, 4, f );
+ if( read != 4 ) {
+ return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
}
+ // check for gzip header
if( ( ( LittleLong( ul ) & 0xe0ffffff ) == 0x00088b1f ) ) {
- Com_DPrintf( "%s: looks like gzip file\n", __func__ );
- if( !FS_FilterFile( f ) ) {
- Com_DPrintf( "%s: couldn't install gzip filter\n", __func__ );
- return -1;
+ ret = FS_FilterFile( f );
+ if( ret ) {
+ return ret;
}
- if( FS_Read( &ul, 4, f ) != 4 ) {
- Com_DPrintf( "%s: short read of msglen\n", __func__ );
- return -1;
+ read = FS_Read( &ul, 4, f );
+ if( read != 4 ) {
+ return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
}
}
+ // determine demo type
if( ul == MVD_MAGIC ) {
- if( FS_Read( &us, 2, f ) != 2 ) {
- Com_DPrintf( "%s: short read of msglen\n", __func__ );
- return -1;
+ read = FS_Read( &us, 2, f );
+ if( read != 2 ) {
+ return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
}
- if( us == ( uint16_t )-1 ) {
- Com_DPrintf( "%s: end of demo\n", __func__ );
- return -1;
+ if( !us ) {
+ return Q_ERR_UNEXPECTED_EOF;
}
msglen = LittleShort( us );
type = 1;
} else {
if( ul == ( uint32_t )-1 ) {
- Com_DPrintf( "%s: end of demo\n", __func__ );
- return -1;
+ return Q_ERR_UNEXPECTED_EOF;
}
msglen = LittleLong( ul );
type = 0;
}
- if( msglen >= sizeof( msg_read_buffer ) ) {
- Com_DPrintf( "%s: bad msglen\n", __func__ );
- return -1;
+ if( msglen < 64 || msglen > sizeof( msg_read_buffer ) ) {
+ return Q_ERR_INVALID_FORMAT;
}
SZ_Init( &msg_read, msg_read_buffer, sizeof( msg_read_buffer ) );
msg_read.cursize = msglen;
// read packet data
- if( FS_Read( msg_read.data, msglen, f ) != msglen ) {
- Com_DPrintf( "%s: short read of data\n", __func__ );
- return -1;
+ read = FS_Read( msg_read.data, msglen, f );
+ if( read != msglen ) {
+ return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
}
return type;
}
-/*
-====================
-CL_ReadNextDemoMessage
-====================
-*/
-static qboolean CL_ReadNextDemoMessage( fileHandle_t f ) {
- uint32_t msglen;
+static int read_next_message( qhandle_t f ) {
+ uint32_t msglen;
+ ssize_t read;
// read msglen
- if( FS_Read( &msglen, 4, f ) != 4 ) {
- Com_DPrintf( "%s: short read of msglen\n", __func__ );
- return qfalse;
+ read = FS_Read( &msglen, 4, f );
+ if( read != 4 ) {
+ return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
}
+ // check for EOF packet
if( msglen == ( uint32_t )-1 ) {
- Com_DPrintf( "%s: end of demo\n", __func__ );
- return qfalse;
+ return 0;
}
msglen = LittleLong( msglen );
- if( msglen >= sizeof( msg_read_buffer ) ) {
- Com_DPrintf( "%s: bad msglen\n", __func__ );
- return qfalse;
+ if( msglen > sizeof( msg_read_buffer ) ) {
+ return Q_ERR_INVALID_FORMAT;
}
SZ_Init( &msg_read, msg_read_buffer, sizeof( msg_read_buffer ) );
msg_read.cursize = msglen;
// read packet data
- if( FS_Read( msg_read.data, msglen, f ) != msglen ) {
- Com_DPrintf( "%s: short read of data\n", __func__ );
- return qfalse;
+ read = FS_Read( msg_read.data, msglen, f );
+ if( read != msglen ) {
+ return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
}
- return qtrue;
+ return 1;
}
-/*
-====================
-CL_ParseNextDemoMessage
-====================
-*/
-static void CL_ParseNextDemoMessage( void ) {
- int pos;
- char *s;
+static void parse_next_message( void ) {
+ int ret;
+
+ ret = read_next_message( cls.demo.playback );
+ if( ret <= 0 ) {
+ char *s = Cvar_VariableString( "nextserver" );
- if( !CL_ReadNextDemoMessage( cls.demo.playback ) ) {
- s = Cvar_VariableString( "nextserver" );
if( !s[0] ) {
- Com_Error( ERR_SILENT, "Demo finished" );
+ if( ret == 0 ) {
+ Com_Error( ERR_SILENT, "Demo finished" );
+ } else {
+ Com_Error( ERR_DROP, "Couldn't read demo: %s", Q_ErrorString( ret ) );
+ }
}
+
+ FS_FCloseFile( cls.demo.playback );
+ memset( &cls.demo, 0, sizeof( cls.demo ) );
+
Cbuf_AddText( &cmd_buffer, s );
Cbuf_AddText( &cmd_buffer, "\n" );
Cvar_Set( "nextserver", "" );
@@ -568,11 +557,11 @@ static void CL_ParseNextDemoMessage( void ) {
CL_ParseServerMessage();
if( cls.demo.file_size ) {
- pos = FS_Tell( cls.demo.playback ) - cls.demo.file_offset;
- if( pos < 0 ) {
- pos = 0;
+ ssize_t pos = FS_Tell( cls.demo.playback );
+
+ if( pos > cls.demo.file_offset ) {
+ cls.demo.file_percent = ( pos - cls.demo.file_offset ) * 100 / cls.demo.file_size;
}
- cls.demo.file_percent = pos * 100 / cls.demo.file_size;
}
}
@@ -583,9 +572,9 @@ CL_PlayDemo_f
*/
static void CL_PlayDemo_f( void ) {
char name[MAX_OSPATH];
- fileHandle_t demofile;
+ qhandle_t demofile;
char *arg;
- size_t length;
+ ssize_t len, ofs;
int type, argc = Cmd_Argc();
if( argc < 2 ) {
@@ -596,20 +585,33 @@ static void CL_PlayDemo_f( void ) {
arg = Cmd_Argv( 1 );
if( arg[0] == '/' ) {
// Assume full path is given
- Q_strlcpy( name, arg + 1, sizeof( name ) );
- FS_FOpenFile( name, &demofile, FS_MODE_READ );
+ len = Q_strlcpy( name, arg + 1, sizeof( name ) );
+ if( len >= sizeof( name ) ) {
+ len = Q_ERR_NAMETOOLONG;
+ goto fail;
+ }
+ len = FS_FOpenFile( name, &demofile, FS_MODE_READ );
} else {
// Search for matching extensions
- Q_concat( name, sizeof( name ), "demos/", arg, NULL );
- FS_FOpenFile( name, &demofile, FS_MODE_READ );
+ len = Q_concat( name, sizeof( name ), "demos/", arg, NULL );
+ if( len >= sizeof( name ) ) {
+ len = Q_ERR_NAMETOOLONG;
+ goto fail;
+ }
+ len = FS_FOpenFile( name, &demofile, FS_MODE_READ );
if( !demofile ) {
- COM_DefaultExtension( name, ".dm2", sizeof( name ) );
- FS_FOpenFile( name, &demofile, FS_MODE_READ );
+ len = COM_DefaultExtension( name, ".dm2", sizeof( name ) );
+ if( len >= sizeof( name ) ) {
+ len = Q_ERR_NAMETOOLONG;
+ goto fail;
+ }
+ len = FS_FOpenFile( name, &demofile, FS_MODE_READ );
}
}
if( !demofile ) {
- Com_Printf( "Couldn't open %s\n", name );
+fail:
+ Com_Printf( "Couldn't open %s: %s\n", name, Q_ErrorString( len ) );
return;
}
@@ -623,15 +625,14 @@ static void CL_PlayDemo_f( void ) {
}
#endif
- type = CL_ReadFirstDemoMessage( demofile );
- if( type == -1 ) {
- Com_Printf( "%s is not a demo file\n", name );
+ type = read_first_message( demofile );
+ if( type < 0 ) {
+ Com_Printf( "Couldn't read %s: %s\n", name, Q_ErrorString( type ) );
FS_FCloseFile( demofile );
return;
}
if( type == 1 ) {
- Com_DPrintf( "%s is a MVD file\n", name );
Cbuf_InsertText( &cmd_buffer, va( "mvdplay --replace @@ /%s\n", name ) );
FS_FCloseFile( demofile );
return;
@@ -656,16 +657,13 @@ static void CL_PlayDemo_f( void ) {
CL_ParseServerMessage();
while( cls.state == ca_connected ) {
Cbuf_Execute( &cl_cmdbuf );
- CL_ParseNextDemoMessage();
+ parse_next_message();
}
- length = FS_GetFileLength( demofile );
- if( length == INVALID_LENGTH ) {
- cls.demo.file_offset = 0;
- cls.demo.file_size = 0;
- } else {
- cls.demo.file_offset = FS_Tell( demofile );
- cls.demo.file_size = length - cls.demo.file_offset;
+ ofs = FS_Tell( demofile );
+ if( ofs > 0 ) {
+ cls.demo.file_offset = ofs;
+ cls.demo.file_size = len - ofs;
}
if( com_timedemo->integer ) {
@@ -680,7 +678,7 @@ static void CL_Demo_c( genctx_t *ctx, int argnum ) {
}
}
-static void CL_ParseInfoString( demoInfo_t *info, int clientNum, int index, const char *string ) {
+static void parse_info_string( demoInfo_t *info, int clientNum, int index, const char *string ) {
size_t len;
char *p;
@@ -707,7 +705,7 @@ CL_GetDemoInfo
====================
*/
demoInfo_t *CL_GetDemoInfo( const char *path, demoInfo_t *info ) {
- fileHandle_t f;
+ qhandle_t f;
int c, index;
char string[MAX_QPATH];
int clientNum, type;
@@ -717,8 +715,8 @@ demoInfo_t *CL_GetDemoInfo( const char *path, demoInfo_t *info ) {
return NULL;
}
- type = CL_ReadFirstDemoMessage( f );
- if( type == -1 ) {
+ type = read_first_message( f );
+ if( type < 0 ) {
goto fail;
}
@@ -738,7 +736,7 @@ demoInfo_t *CL_GetDemoInfo( const char *path, demoInfo_t *info ) {
while( 1 ) {
c = MSG_ReadByte();
if( c == -1 ) {
- if( !CL_ReadNextDemoMessage( f ) ) {
+ if( read_next_message( f ) <= 0 ) {
break;
}
continue; // parse new message
@@ -751,7 +749,7 @@ demoInfo_t *CL_GetDemoInfo( const char *path, demoInfo_t *info ) {
goto fail;
}
MSG_ReadString( string, sizeof( string ) );
- CL_ParseInfoString( info, clientNum, index, string );
+ parse_info_string( info, clientNum, index, string );
}
} else {
if( MSG_ReadByte() != mvd_serverdata ) {
@@ -774,7 +772,7 @@ demoInfo_t *CL_GetDemoInfo( const char *path, demoInfo_t *info ) {
goto fail;
}
MSG_ReadString( string, sizeof( string ) );
- CL_ParseInfoString( info, clientNum, index, string );
+ parse_info_string( info, clientNum, index, string );
}
}
@@ -800,19 +798,19 @@ void CL_DemoFrame( void ) {
return;
}
if( cls.state != ca_active ) {
- CL_ParseNextDemoMessage();
+ parse_next_message();
return;
}
if( com_timedemo->integer ) {
- CL_ParseNextDemoMessage();
+ parse_next_message();
cl.time = cl.servertime;
cls.demo.time_frames++;
return;
}
while( cl.servertime < cl.time ) {
- CL_ParseNextDemoMessage();
+ parse_next_message();
if( cls.state != ca_active ) {
break;
}
diff --git a/source/cl_http.c b/source/cl_http.c
index 4729a35..3e01908 100644
--- a/source/cl_http.c
+++ b/source/cl_http.c
@@ -241,6 +241,7 @@ static void start_download (dlqueue_t *entry, dlhandle_t *dl) {
char temp[MAX_QPATH];
char escaped[MAX_QPATH*4];
CURLMcode ret;
+ qerror_t err;
//yet another hack to accomodate filelists, how i wish i could push :(
//NULL file handle indicates filelist.
@@ -263,12 +264,16 @@ static void start_download (dlqueue_t *entry, dlhandle_t *dl) {
}
escape_path (temp, escaped);
- FS_CreatePath (dl->path);
+ err = FS_CreatePath (dl->path);
+ if (err < 0) {
+ Com_EPrintf ("[HTTP] Couldn't create path to '%s': %s\n", dl->path, Q_ErrorString (err));
+ goto fail;
+ }
//don't bother with http resume... too annoying if server doesn't support it.
dl->file = fopen (dl->path, "wb");
if (!dl->file) {
- Com_EPrintf ("[HTTP] Couldn't open '%s' for writing.\n", dl->path);
+ Com_EPrintf ("[HTTP] Couldn't open '%s' for writing: %s\n", dl->path, strerror (errno));
goto fail;
}
}
@@ -639,7 +644,7 @@ static void check_and_queue_download (char *path) {
return;
}
- if (FS_LoadFileEx (path, NULL, flags, TAG_FREE) == INVALID_LENGTH) {
+ if (!FS_FileExistsEx (path, flags)) {
queue_download (path, type);
}
}
@@ -684,7 +689,7 @@ static void rescan_queue (void) {
FOR_EACH_DLQ (q) {
if (q->state == DL_PENDING) {
- if (q->type == DL_OTHER && FS_LoadFile (q->path, NULL) != INVALID_LENGTH)
+ if (q->type == DL_OTHER && FS_FileExists (q->path))
q->state = DL_DONE;
else
pending_count++;
@@ -831,8 +836,8 @@ fatal2:
Q_snprintf (temp, sizeof(temp), "%s/%s", fs_gamedir, dl->queue->path);
if (rename (dl->path, temp))
- Com_EPrintf ("[HTTP] Failed to rename '%s' to '%s'\n",
- dl->path, dl->queue->path);
+ Com_EPrintf ("[HTTP] Failed to rename '%s' to '%s': %s\n",
+ dl->path, dl->queue->path, strerror (errno));
dl->path[0] = 0;
//a pak file is very special...
diff --git a/source/cl_keys.c b/source/cl_keys.c
index c131b25..19c1a6e 100644
--- a/source/cl_keys.c
+++ b/source/cl_keys.c
@@ -492,7 +492,7 @@ Key_WriteBindings
Writes lines containing "bind key value"
============
*/
-void Key_WriteBindings( fileHandle_t f ) {
+void Key_WriteBindings( qhandle_t f ) {
int i;
for( i = 0; i < 256; i++ ) {
diff --git a/source/cl_local.h b/source/cl_local.h
index 8ba2f9f..78fe9bb 100644
--- a/source/cl_local.h
+++ b/source/cl_local.h
@@ -257,7 +257,6 @@ typedef struct client_static_s {
qboolean ref_initialized;
unsigned disable_screen;
- qboolean _unused1;
int userinfo_modified;
cvar_t *userinfo_updates[MAX_PACKET_USERINFOS];
@@ -286,49 +285,46 @@ typedef struct client_static_s {
// connection information
netadr_t serverAddress;
- char servername[MAX_OSPATH]; // name of server from original connect
- unsigned connect_time; // for connection retransmits
+ char servername[MAX_OSPATH]; // name of server from original connect
+ unsigned connect_time; // for connection retransmits
int connect_count;
-#if USE_AC_CLIENT
- char messageString[MAX_STRING_CHARS];
-#endif
qboolean passive;
#if USE_ZLIB
z_stream z;
#endif
- int quakePort; // a 16 bit value that allows quake servers
+ int quakePort; // a 16 bit value that allows quake servers
// to work around address translating routers
netchan_t *netchan;
- int serverProtocol; // in case we are doing some kind of version hack
+ int serverProtocol; // in case we are doing some kind of version hack
int protocolVersion; // minor version
- int challenge; // from the server to use for connecting
+ int challenge; // from the server to use for connecting
+
+#if USE_ICMP
+ qboolean errorReceived; // got an ICMP error from server
+#endif
struct {
- fileHandle_t file; // file transfer from server
- char temp[MAX_QPATH+4]; // account 4 bytes for .tmp suffix
- char name[MAX_QPATH];
- int percent;
+ qhandle_t file; // file transfer from server
+ char temp[MAX_QPATH+4]; // account 4 bytes for .tmp suffix
+ char name[MAX_QPATH];
+ int percent;
} download;
// demo recording info must be here, so it isn't cleared on level change
struct {
- fileHandle_t playback;
- fileHandle_t recording;
- unsigned time_start;
- unsigned time_frames;
- int file_size;
- int file_offset;
- int file_percent;
- sizebuf_t buffer;
- qboolean paused;
+ qhandle_t playback;
+ qhandle_t recording;
+ unsigned time_start;
+ unsigned time_frames;
+ int file_size;
+ int file_offset;
+ int file_percent;
+ sizebuf_t buffer;
+ qboolean paused;
} demo;
-
-#if USE_ICMP
- qboolean errorReceived; // got an ICMP error from server
-#endif
} client_static_t;
extern client_static_t cls;
@@ -750,7 +746,7 @@ void CL_AddNetgraph (void);
void Key_Init( void );
void Key_Event( unsigned key, qboolean down, unsigned time );
void Key_CharEvent( int key );
-void Key_WriteBindings( fileHandle_t f );
+void Key_WriteBindings( qhandle_t f );
//
diff --git a/source/cl_locs.c b/source/cl_locs.c
index 886846b..71cef2d 100644
--- a/source/cl_locs.c
+++ b/source/cl_locs.c
@@ -67,13 +67,16 @@ void LOC_LoadLocations( void ) {
int line, count;
location_t *loc;
int argc;
+ qerror_t ret;
// load from main directory
Q_concat( path, sizeof( path ), "locs/", cl.mapname, ".loc", NULL );
- FS_LoadFile( path, (void **)&buffer );
+ ret = FS_LoadFile( path, (void **)&buffer );
if( !buffer ) {
- Com_DPrintf( "Couldn't load %s\n", path );
+ if( ret != Q_ERR_NOENT ) {
+ Com_EPrintf( "Couldn't load %s: %s\n", path, Q_ErrorString( ret ) );
+ }
return;
}
@@ -109,7 +112,6 @@ void LOC_LoadLocations( void ) {
Com_Printf( "Loaded %i locations from %s\n", count, path );
FS_FreeFile( buffer );
-
}
/*
diff --git a/source/cl_main.c b/source/cl_main.c
index 00ed437..1f6903e 100644
--- a/source/cl_main.c
+++ b/source/cl_main.c
@@ -1928,20 +1928,19 @@ void CL_RequestNextDownload ( void ) {
#endif
if ( precache_check == ENV_CNT ) {
- precache_check = ENV_CNT + 1;
-
- if( ( cl.bsp = BSP_Load( cl.configstrings[ CS_MODELS + 1 ] ) ) == NULL ) {
+ qerror_t ret = BSP_Load( cl.configstrings[ CS_MODELS + 1 ], &cl.bsp );
+ if( cl.bsp == NULL ) {
Com_Error( ERR_DROP, "Couldn't load %s: %s",
- cl.configstrings[ CS_MODELS + 1 ], BSP_GetError() );
+ cl.configstrings[ CS_MODELS + 1 ], Q_ErrorString( ret ) );
}
#if USE_MAPCHECKSUM
if ( cl.bsp->checksum != atoi( cl.configstrings[ CS_MAPCHECKSUM ] ) ) {
- Com_Error ( ERR_DROP, "Local map version differs from server: %i != '%s'\n",
- cl.bsp->checksum, cl.configstrings[ CS_MAPCHECKSUM ] );
- return;
+ Com_Error ( ERR_DROP, "Local map version differs from server: %i != %s",
+ cl.bsp->checksum, cl.configstrings[ CS_MAPCHECKSUM ] );
}
#endif
+ precache_check = ENV_CNT + 1;
CL_RegisterModels();
}
@@ -1976,9 +1975,9 @@ void CL_RequestNextDownload ( void ) {
// Also check if 32bit images are present
Q_concat( fn, sizeof( fn ), "textures/", texname, ".jpg", NULL );
- if ( FS_LoadFile( fn, NULL ) == INVALID_LENGTH ) {
+ if ( !FS_FileExists( fn ) ) {
Q_concat( fn, sizeof( fn ), "textures/", texname, ".tga", NULL );
- if ( FS_LoadFile( fn, NULL ) == INVALID_LENGTH ) {
+ if ( !FS_FileExists( fn ) ) {
Q_concat( fn, sizeof( fn ), "textures/", texname, ".wal", NULL );
if ( !CL_CheckOrDownloadFile( fn ) ) {
return; // started a download
@@ -2044,9 +2043,10 @@ static void CL_Precache_f( void ) {
//Yet another hack to let old demos work
//the old precache sequence
if( cls.demo.playback ) {
- if( ( cl.bsp = BSP_Load( cl.configstrings[ CS_MODELS + 1 ] ) ) == NULL ) {
+ qerror_t ret = BSP_Load( cl.configstrings[ CS_MODELS + 1 ], &cl.bsp );
+ if( cl.bsp == NULL ) {
Com_Error( ERR_DROP, "Couldn't load %s: %s",
- cl.configstrings[ CS_MODELS + 1 ], BSP_GetError() );
+ cl.configstrings[ CS_MODELS + 1 ], Q_ErrorString( ret ) );
}
CL_RegisterModels();
CL_PrepRefresh();
@@ -2087,8 +2087,8 @@ static void CL_DumpClients_f( void ) {
static void dump_program( const char *text, const char *name ) {
char buffer[MAX_OSPATH];
- fileHandle_t f;
size_t len;
+ qerror_t ret;
if( cls.state != ca_active ) {
Com_Printf( "Must be in a level to dump.\n" );
@@ -2111,16 +2111,13 @@ static void dump_program( const char *text, const char *name ) {
return;
}
- FS_FOpenFile( buffer, &f, FS_MODE_WRITE );
- if( !f ) {
- Com_EPrintf( "Couldn't open %s for writing.\n", buffer );
+ len = strlen( text );
+ ret = FS_WriteFile( buffer, text, len );
+ if( ret < 0 ) {
+ Com_EPrintf( "Couldn't write %s: %s\n", buffer, Q_ErrorString( ret ) );
return;
}
- FS_FPrintf( f, "%s\n", text );
-
- FS_FCloseFile( f );
-
Com_Printf( "Dumped %s program to %s.\n", name, buffer );
}
@@ -2154,8 +2151,7 @@ static void CL_WriteConfig_f( void ) {
char buffer[MAX_OSPATH];
qboolean aliases = qfalse, bindings = qfalse, modified = qfalse;
int c, mask = 0;
- fileHandle_t f;
- size_t len;
+ qhandle_t f;
while( ( c = Cmd_ParseOptions( o_writeconfig ) ) != -1 ) {
switch( c ) {
@@ -2193,15 +2189,9 @@ static void CL_WriteConfig_f( void ) {
mask = CVAR_ARCHIVE;
}
- len = Q_concat( buffer, sizeof( buffer ), "configs/", cmd_optarg, ".cfg", NULL );
- if( len >= sizeof( buffer ) ) {
- Com_EPrintf( "Oversize filename specified.\n" );
- return;
- }
-
- FS_FOpenFile( buffer, &f, FS_MODE_WRITE );
+ f = FS_EasyOpenFile( buffer, sizeof( buffer ), FS_MODE_WRITE,
+ "configs/", cmd_optarg, ".cfg" );
if( !f ) {
- Com_Printf( "Couldn't open %s for writing\n", buffer );
return;
}
@@ -3120,10 +3110,13 @@ Writes key bindings and archived cvars to config.cfg
===============
*/
static void CL_WriteConfig( void ) {
- fileHandle_t f;
+ qhandle_t f;
+ qerror_t ret;
- FS_FOpenFile( COM_CONFIG_NAME, &f, FS_MODE_WRITE );
+ ret = FS_FOpenFile( COM_CONFIG_NAME, &f, FS_MODE_WRITE );
if( !f ) {
+ Com_EPrintf( "Couldn't open %s for writing: %s\n",
+ COM_CONFIG_NAME, Q_ErrorString( ret ) );
return;
}
diff --git a/source/cl_parse.c b/source/cl_parse.c
index f37872c..0423066 100644
--- a/source/cl_parse.c
+++ b/source/cl_parse.c
@@ -32,8 +32,9 @@ to start a download from the server.
===============
*/
qboolean CL_CheckOrDownloadFile( const char *path ) {
- fileHandle_t f;
+ qhandle_t f;
size_t len;
+ ssize_t ret;
len = strlen( path );
if( len < 1 || len >= MAX_QPATH
@@ -48,7 +49,7 @@ qboolean CL_CheckOrDownloadFile( const char *path ) {
return qtrue;
}
- if( FS_LoadFile( path, NULL ) != INVALID_LENGTH ) {
+ if( FS_FileExists( path ) ) {
// it exists, no need to download
return qtrue;
}
@@ -73,20 +74,19 @@ qboolean CL_CheckOrDownloadFile( const char *path ) {
//ZOID
// check to see if we already have a tmp for this file, if so, try to resume
// open the file if not opened yet
- len = FS_FOpenFile( cls.download.temp, &f, FS_MODE_RDWR );
- if( len == INVALID_LENGTH && f ) {
- Com_WPrintf( "Couldn't determine size of %s\n", cls.download.temp );
- FS_FCloseFile( f );
- f = 0;
- }
- if( f ) { // it exists
+ ret = FS_FOpenFile( cls.download.temp, &f, FS_MODE_RDWR );
+ if( ret >= 0 ) { // it exists
cls.download.file = f;
// give the server an offset to start the download
Com_Printf( "Resuming %s\n", cls.download.name );
- CL_ClientCommand( va( "download \"%s\" %"PRIz, cls.download.name, len ) );
- } else {
+ CL_ClientCommand( va( "download \"%s\" %d", cls.download.name, (int)ret ) );
+ } else if( ret == Q_ERR_NOENT ) { // it doesn't exist
Com_Printf( "Downloading %s\n", cls.download.name );
CL_ClientCommand( va( "download \"%s\"", cls.download.name ) );
+ } else { // error happened
+ Com_EPrintf( "Couldn't open %s for appending: %s\n", cls.download.temp,
+ Q_ErrorString( ret ) );
+ return qtrue;
}
return qfalse;
@@ -126,8 +126,8 @@ void CL_Download_f( void ) {
path = Cmd_Argv( 1 );
- if( FS_LoadFile( path, NULL ) != INVALID_LENGTH ) {
- Com_Printf( "File '%s' already exists.\n", path );
+ if( FS_FileExists( path ) ) {
+ Com_Printf( "%s already exists.\n", path );
return;
}
@@ -143,7 +143,8 @@ A download message has been received from the server
=====================
*/
static void CL_ParseDownload( void ) {
- int size, percent;
+ int size, percent;
+ qerror_t ret;
if( !cls.download.temp[0] ) {
Com_Error( ERR_DROP, "%s: no download requested", __func__ );
@@ -175,11 +176,11 @@ static void CL_ParseDownload( void ) {
// open the file if not opened yet
if( !cls.download.file ) {
- FS_FOpenFile( cls.download.temp, &cls.download.file, FS_MODE_WRITE );
+ ret = FS_FOpenFile( cls.download.temp, &cls.download.file, FS_MODE_WRITE );
if( !cls.download.file ) {
msg_read.readcount += size;
- Com_WPrintf( "Failed to open '%s' for writing\n",
- cls.download.temp );
+ Com_EPrintf( "Couldn't open %s for writing: %s\n",
+ cls.download.temp, Q_ErrorString( ret ) );
goto another;
}
}
@@ -197,9 +198,10 @@ static void CL_ParseDownload( void ) {
FS_FCloseFile( cls.download.file );
// rename the temp file to it's final name
- if( !FS_RenameFile( cls.download.temp, cls.download.name ) ) {
- Com_WPrintf( "Failed to rename %s to %s\n",
- cls.download.temp, cls.download.name );
+ ret = FS_RenameFile( cls.download.temp, cls.download.name );
+ if( ret ) {
+ Com_EPrintf( "Couldn't rename %s to %s: %s\n",
+ cls.download.temp, cls.download.name, Q_ErrorString( ret ) );
}
Com_Printf( "Downloaded successfully.\n" );
diff --git a/source/cl_public.h b/source/cl_public.h
index f965545..65cedad 100644
--- a/source/cl_public.h
+++ b/source/cl_public.h
@@ -125,7 +125,7 @@ void IN_WarpMouse( int x, int y );
void Key_Init( void );
void Key_Event( unsigned key, qboolean down, unsigned time );
void Key_CharEvent( int key );
-void Key_WriteBindings( fileHandle_t f );
+void Key_WriteBindings( qhandle_t f );
char *VID_GetClipboardData( void );
void VID_SetClipboardData( const char *data );
diff --git a/source/cmd.c b/source/cmd.c
index 711ba98..51f56ed 100644
--- a/source/cmd.c
+++ b/source/cmd.c
@@ -378,7 +378,7 @@ static void Cmd_UnAlias_f( void ) {
}
#if USE_CLIENT
-void Cmd_WriteAliases( fileHandle_t f ) {
+void Cmd_WriteAliases( qhandle_t f ) {
cmdalias_t *a;
LIST_FOR_EACH( cmdalias_t, a, &cmd_alias, listEntry ) {
@@ -1346,7 +1346,7 @@ Cmd_Exec_f
static void Cmd_Exec_f( void ) {
char buffer[MAX_QPATH];
char *f;
- size_t len;
+ ssize_t len;
int i;
if( Cmd_Argc() != 2 ) {
@@ -1362,7 +1362,7 @@ static void Cmd_Exec_f( void ) {
COM_DefaultExtension( buffer, ".cfg", sizeof( buffer ) );
len = FS_LoadFile( buffer, ( void ** )&f );
if( !f ) {
- Com_Printf( "Couldn't exec %s\n", buffer );
+ Com_Printf( "Couldn't exec %s: %s\n", buffer, Q_ErrorString( len ) );
return;
}
}
diff --git a/source/cmodel.c b/source/cmodel.c
index 1234a77..60ca284 100644
--- a/source/cmodel.c
+++ b/source/cmodel.c
@@ -58,11 +58,13 @@ CM_LoadMap
Loads in the map and all submodels
==================
*/
-qboolean CM_LoadMap( cm_t *cm, const char *name ) {
+qerror_t CM_LoadMap( cm_t *cm, const char *name ) {
bsp_t *cache;
+ qerror_t ret;
- if( !( cache = BSP_Load( name ) ) ) {
- return qfalse;
+ ret = BSP_Load( name, &cache );
+ if( !cache ) {
+ return ret;
}
cm->cache = cache;
@@ -71,7 +73,7 @@ qboolean CM_LoadMap( cm_t *cm, const char *name ) {
cm->portalopen = ( qboolean * )( cm->floodnums + cm->cache->numareas );
FloodAreaConnections( cm );
- return qtrue;
+ return Q_ERR_SUCCESS;
}
mnode_t *CM_NodeNum( cm_t *cm, int number ) {
diff --git a/source/cmodel.h b/source/cmodel.h
index 22a7f6f..49621fe 100644
--- a/source/cmodel.h
+++ b/source/cmodel.h
@@ -36,7 +36,7 @@ typedef struct {
void CM_Init( void );
void CM_FreeMap( cm_t *cm );
-qboolean CM_LoadMap( cm_t *cm, const char *name );
+qerror_t CM_LoadMap( cm_t *cm, const char *name );
int CM_NumClusters( cm_t *cm );
int CM_NumInlineModels( cm_t *cm );
@@ -85,6 +85,6 @@ int CM_WritePortalBits( cm_t *cm, byte *buffer );
void CM_SetPortalStates( cm_t *cm, byte *buffer, int bytes );
qboolean CM_HeadnodeVisible( mnode_t *headnode, byte *visbits );
-void CM_WritePortalState( cm_t *cm, fileHandle_t f );
-void CM_ReadPortalState( cm_t *cm, fileHandle_t f );
+void CM_WritePortalState( cm_t *cm, qhandle_t f );
+void CM_ReadPortalState( cm_t *cm, qhandle_t f );
diff --git a/source/com_local.h b/source/com_local.h
index ae5d7bc..a94a2ec 100644
--- a/source/com_local.h
+++ b/source/com_local.h
@@ -22,6 +22,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <config.h>
#include "q_shared.h"
+#include <errno.h>
+#include "error.h"
#if USE_CLIENT
#define APPLICATION "q2pro"
@@ -207,7 +209,7 @@ void Cmd_Shift( void );
void Cmd_Alias_f( void );
-void Cmd_WriteAliases( fileHandle_t f );
+void Cmd_WriteAliases( qhandle_t f );
#define EXEC_TRIGGER( var ) \
do { \
@@ -301,7 +303,7 @@ void Cvar_Command( cvar_t *v );
// command. Returns qtrue if the command was a variable reference that
// was handled. (print or change)
-void Cvar_WriteVariables( fileHandle_t f, int mask, qboolean modified );
+void Cvar_WriteVariables( qhandle_t f, int mask, qboolean modified );
// appends lines containing "set variable value" for all variables
// with matching flags
@@ -597,7 +599,7 @@ extern unsigned com_framenum;
extern qboolean com_initialized;
extern time_t com_startTime;
-extern fileHandle_t com_logFile;
+extern qhandle_t com_logFile;
#if USE_CLIENT || USE_MVD_CLIENT || USE_MVD_SERVER
extern const cmd_option_t o_record[];
diff --git a/source/common.c b/source/common.c
index 7494b4e..ed77a85 100644
--- a/source/common.c
+++ b/source/common.c
@@ -84,8 +84,8 @@ cvar_t *allow_download_others;
cvar_t *rcon_password;
-fileHandle_t com_logFile;
-qboolean com_logNewline;
+qhandle_t com_logFile;
+qboolean com_logNewline;
unsigned com_framenum;
unsigned com_eventTime;
unsigned com_localTime;
@@ -178,29 +178,22 @@ static void logfile_close( void ) {
static void logfile_open( void ) {
char buffer[MAX_OSPATH];
- size_t len;
- int mode;
-
- len = Q_concat( buffer, sizeof( buffer ), "logs/",
- logfile_name->string, ".log", NULL );
- if( len >= sizeof( buffer ) ) {
- Com_WPrintf( "Oversize logfile name specified\n" );
- Cvar_Set( "logfile", "0" );
- return;
- }
+ unsigned mode;
+ qhandle_t f;
mode = logfile_enable->integer > 1 ? FS_MODE_APPEND : FS_MODE_WRITE;
if( logfile_flush->integer ) {
mode |= FS_FLUSH_SYNC;
}
- FS_FOpenFile( buffer, &com_logFile, mode );
- if( !com_logFile ) {
- Com_WPrintf( "Couldn't open %s for writing\n", buffer );
+ f = FS_EasyOpenFile( buffer, sizeof( buffer ), mode,
+ "logs/", logfile_name->string, ".log" );
+ if( !f ) {
Cvar_Set( "logfile", "0" );
return;
}
+ com_logFile = f;
com_logNewline = qtrue;
Com_Printf( "Logging console to %s\n", buffer );
}
diff --git a/source/cvar.c b/source/cvar.c
index 2daaa57..7c1e425 100644
--- a/source/cvar.c
+++ b/source/cvar.c
@@ -307,8 +307,15 @@ void Cvar_SetByVar( cvar_t *var, const char *value, from_t from ) {
if( !value ) {
value = "";
}
- if( !strcmp( value, var->string ) && !( var->flags & CVAR_LATCH ) ) {
- return; // not changed
+ if( !strcmp( value, var->string ) ) {
+ if( var->flags & CVAR_LATCH ) {
+ // set back to current value
+ if( var->latched_string ) {
+ Z_Free( var->latched_string );
+ var->latched_string = NULL;
+ }
+ }
+ return; // not changed
}
if( var->flags & CVAR_INFOMASK ) {
@@ -340,15 +347,6 @@ void Cvar_SetByVar( cvar_t *var, const char *value, from_t from ) {
}
if( var->flags & CVAR_LATCH ) {
- if( !strcmp( var->string, value ) ) {
- // set back to current value
- if( var->latched_string ) {
- Z_Free( var->latched_string );
- var->latched_string = NULL;
- }
- return;
- }
-
// free latched value
if( var->latched_string ) {
if( !strcmp( var->latched_string, value ) ) {
@@ -722,7 +720,7 @@ Appends lines containing "set variable value" for all variables
with the archive flag set to true.
============
*/
-void Cvar_WriteVariables( fileHandle_t f, int mask, qboolean modified ) {
+void Cvar_WriteVariables( qhandle_t f, int mask, qboolean modified ) {
cvar_t *var;
char *string;
diff --git a/source/error.c b/source/error.c
new file mode 100644
index 0000000..bc61794
--- /dev/null
+++ b/source/error.c
@@ -0,0 +1,72 @@
+/*
+Copyright (C) 2010 skuller.net
+
+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 2
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include <config.h>
+#include "q_shared.h"
+#include <errno.h>
+#include "error.h"
+
+static const char *const error_table[] = {
+ "Unspecified error",
+ "Unknown file format",
+ "Invalid file format",
+ "Bad lump extent",
+ "Odd lump size",
+ "Too many elements",
+ "Too few elements",
+ "Index out of range",
+ "Invalid quake path",
+ "Unclean quake path",
+ "Unexpected end of file",
+ "File too small",
+ "Bad run length packet",
+ "String truncated",
+ "Library error",
+#if USE_ZLIB
+ "Inflate failed",
+ "Deflate failed",
+ "Coherency check failed",
+#endif
+};
+
+static const int num_errors =
+ sizeof( error_table ) / sizeof( error_table[0] );
+
+const char *Q_ErrorString( qerror_t error ) {
+ int e;
+
+ if( error >= 0 ) {
+ return "Success";
+ }
+
+ if( error > -ERRNO_MAX ) {
+#if EINVAL > 0
+ e = -error;
+#else
+ e = error;
+#endif
+ return strerror( e );
+ }
+
+ e = _Q_ERR( error );
+
+ return error_table[e >= num_errors ? 0 : e];
+}
+
diff --git a/source/error.h b/source/error.h
new file mode 100644
index 0000000..dab50cc
--- /dev/null
+++ b/source/error.h
@@ -0,0 +1,65 @@
+/*
+Copyright (C) 2010 skuller.net
+
+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 2
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#define ERRNO_MAX 20000
+#if EINVAL > 0
+#define Q_ERRNO(e) ((e == Q_ERR_SUCCESS || e < -ERRNO_MAX) ? 0 : -e)
+#define Q_ERR(e) (e == 0 ? Q_ERR_SUCCESS : e > ERRNO_MAX ? -ERRNO_MAX : -e)
+#else
+#define Q_ERRNO(e) ((e == Q_ERR_SUCCESS || e < -ERRNO_MAX) ? 0 : e)
+#define Q_ERR(e) (e == 0 ? Q_ERR_SUCCESS : e < -ERRNO_MAX ? -ERRNO_MAX : e)
+#endif
+#define _Q_ERR(e) (-ERRNO_MAX-e)
+
+#define Q_ERR_SUCCESS 0 // Success
+#define Q_ERR_FAILURE _Q_ERR(0) // Unspecified error
+#define Q_ERR_UNKNOWN_FORMAT _Q_ERR(1) // Unknown file format
+#define Q_ERR_INVALID_FORMAT _Q_ERR(2) // Invalid file format
+#define Q_ERR_BAD_EXTENT _Q_ERR(3) // Bad lump extent
+#define Q_ERR_ODD_SIZE _Q_ERR(4) // Odd lump size
+#define Q_ERR_TOO_MANY _Q_ERR(5) // Too many elements
+#define Q_ERR_TOO_FEW _Q_ERR(6) // Too few elements
+#define Q_ERR_BAD_INDEX _Q_ERR(7) // Index out of range
+#define Q_ERR_INVALID_PATH _Q_ERR(8) // Invalid quake path
+#define Q_ERR_UNCLEAN_PATH _Q_ERR(9) // Unclean quake path
+#define Q_ERR_UNEXPECTED_EOF _Q_ERR(10) // Unexpected end of file
+#define Q_ERR_FILE_TOO_SMALL _Q_ERR(11) // File too small
+#define Q_ERR_BAD_RLE_PACKET _Q_ERR(12) // Bad run length packet
+#define Q_ERR_STRING_TRUNCATED _Q_ERR(13) // String truncated
+#define Q_ERR_LIBRARY_ERROR _Q_ERR(14) // Library error
+#if USE_ZLIB
+#define Q_ERR_INFLATE_FAILED _Q_ERR(15) // Inflate failed
+#define Q_ERR_DEFLATE_FAILED _Q_ERR(16) // Deflate failed
+#define Q_ERR_NOT_COHERENT _Q_ERR(17) // Coherency check failed
+#endif
+
+#define Q_ERR_NOENT Q_ERR(ENOENT)
+#define Q_ERR_NAMETOOLONG Q_ERR(ENAMETOOLONG)
+#define Q_ERR_INVAL Q_ERR(EINVAL)
+#define Q_ERR_DEADLOCK Q_ERR(EDEADLK)
+#define Q_ERR_NOSYS Q_ERR(ENOSYS)
+#define Q_ERR_SPIPE Q_ERR(ESPIPE)
+#define Q_ERR_FBIG Q_ERR(EFBIG)
+#define Q_ERR_ISDIR Q_ERR(EISDIR)
+#define Q_ERR_AGAIN Q_ERR(EAGAIN)
+#define Q_ERR_NFILE Q_ERR(ENFILE)
+#define Q_ERR_EXIST Q_ERR(EEXIST)
+
+const char *Q_ErrorString( qerror_t error );
diff --git a/source/files.c b/source/files.c
index fa2314d..943be95 100644
--- a/source/files.c
+++ b/source/files.c
@@ -55,10 +55,6 @@ QUAKE FILESYSTEM
#define MAX_READ 0x40000 // read in blocks of 256k
#define MAX_WRITE 0x40000 // write in blocks of 256k
-// protection from malicious paks causing memory exhaustion
-// no loadable Q2 resource should ever exceed this limit
-#define MAX_LOADFILE 0x400000 // 64 MiB
-
#if USE_ZLIB
#define ZIP_MAXFILES 0x4000 // 16k files, rather arbitrary
#define ZIP_BUFSIZE 0x10000 // inflate in blocks of 64k
@@ -101,6 +97,7 @@ typedef struct {
z_stream stream;
size_t rest_in;
size_t rest_out;
+ qerror_t error;
byte buffer[ZIP_BUFSIZE];
} zipstream_t;
#endif
@@ -169,8 +166,6 @@ static symlink_t *fs_links;
static file_t fs_files[MAX_FILE_HANDLES];
-static qboolean fs_fileFromPak;
-
#ifdef _DEBUG
static int fs_count_read, fs_count_strcmp, fs_count_open;
#endif
@@ -187,8 +182,8 @@ static zipstream_t fs_zipstream;
static void open_zip_file( file_t *file );
static void close_zip_file( file_t *file );
-static size_t tell_zip_file( file_t *file );
-static size_t read_zip_file( file_t *file, void *buf, size_t len );
+static ssize_t tell_zip_file( file_t *file );
+static ssize_t read_zip_file( file_t *file, void *buf, size_t len );
#endif
// for tracking users of pack_t instance
@@ -201,16 +196,11 @@ static void pack_put( pack_t *pack );
All of Quake's data access is through a hierchal file system,
but the contents of the file system can be transparently merged from several sources.
-The "base directory" is the path to the directory holding the quake.exe and all game directories.
-The sys_* files pass this to host_init in quakeparms_t->basedir.
-This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory.
+The "base directory" is the path to the directory holding all game directories.
The base directory is only used during filesystem initialization.
The "game directory" is the first tree on the search path and directory that
all generated files (savegames, screenshots, demos, config files) will be saved to.
-This can be overridden with the "-game" command line parameter.
-The game directory can never be changed while quake is executing.
-This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't.
*/
@@ -264,12 +254,30 @@ int FS_pathcmpn( const char *s1, const char *s2, size_t n ) {
return 0; /* strings are equal */
}
+#ifdef _WIN32
/*
================
-FS_AllocHandle
+FS_ReplaceSeparators
================
*/
-static file_t *FS_AllocHandle( fileHandle_t *f ) {
+char *FS_ReplaceSeparators( char *s, int separator ) {
+ char *p;
+
+ p = s;
+ while( *p ) {
+ if( *p == '/' || *p == '\\' ) {
+ *p = separator;
+ }
+ p++;
+ }
+
+ return s;
+}
+#endif
+
+// =============================================================================
+
+static file_t *alloc_handle( qhandle_t *f ) {
file_t *file;
int i;
@@ -283,12 +291,7 @@ static file_t *FS_AllocHandle( fileHandle_t *f ) {
return NULL;
}
-/*
-================
-FS_FileForHandle
-================
-*/
-static file_t *FS_FileForHandle( fileHandle_t f ) {
+static file_t *file_for_handle( qhandle_t f ) {
file_t *file;
if( f <= 0 || f >= MAX_FILE_HANDLES + 1 ) {
@@ -303,38 +306,37 @@ static file_t *FS_FileForHandle( fileHandle_t f ) {
return file;
}
-/*
-================
-FS_ValidatePath
-================
-*/
-static qboolean FS_ValidatePath( const char *s ) {
+static qerror_t validate_path( const char *s ) {
const char *start;
// check for leading slash
// check for empty path
if( *s == '/' || *s == '\\' /*|| *s == 0*/ ) {
- return qfalse;
+ return Q_ERR_INVALID_PATH;
}
start = s;
while( *s ) {
+ // check for high bit
+ if( *s & 128 ) {
+ return Q_ERR_UNCLEAN_PATH;
+ }
// check for ".."
if( *s == '.' && s[1] == '.' ) {
- return qfalse;
+ return Q_ERR_INVALID_PATH;
}
if( *s == '/' || *s == '\\' ) {
// check for two slashes in a row
// check for trailing slash
if( ( s[1] == '/' || s[1] == '\\' || s[1] == 0 ) ) {
- return qfalse;
+ return Q_ERR_INVALID_PATH;
}
}
#ifdef _WIN32
if( *s == ':' ) {
// check for "X:\"
if( s[1] == '\\' || s[1] == '/' ) {
- return qfalse;
+ return Q_ERR_INVALID_PATH;
}
}
#endif
@@ -343,32 +345,11 @@ static qboolean FS_ValidatePath( const char *s ) {
// check length
if( s - start > MAX_OSPATH ) {
- return qfalse;
- }
-
- return qtrue;
-}
-
-#ifdef _WIN32
-/*
-================
-FS_ReplaceSeparators
-================
-*/
-char *FS_ReplaceSeparators( char *s, int separator ) {
- char *p;
-
- p = s;
- while( *p ) {
- if( *p == '/' || *p == '\\' ) {
- *p = separator;
- }
- p++;
+ return Q_ERR_NAMETOOLONG;
}
- return s;
+ return Q_ERR_SUCCESS;
}
-#endif
/*
================
@@ -377,17 +358,19 @@ FS_GetFileLength
Returns:
- current length for files opened for writing.
- cached length for files opened for reading.
-- INVALID_LENGTH for gzip-compressed files.
+- error for gzip-compressed files.
================
*/
-size_t FS_GetFileLength( fileHandle_t f ) {
- file_t *file = FS_FileForHandle( f );
+ssize_t FS_GetFileLength( qhandle_t f ) {
+ file_t *file = file_for_handle( f );
file_info_t info;
+ qerror_t ret;
switch( file->type ) {
case FS_REAL:
- if( !Sys_GetFileInfo( file->fp, &info ) ) {
- return INVALID_LENGTH;
+ ret = Sys_GetFileInfo( file->fp, &info );
+ if( ret ) {
+ return ret;
}
return info.size;
case FS_PAK:
@@ -395,15 +378,9 @@ size_t FS_GetFileLength( fileHandle_t f ) {
case FS_ZIP:
#endif
return file->length;
-#if USE_ZLIB
- case FS_GZ:
- return INVALID_LENGTH;
-#endif
default:
- Com_Error( ERR_FATAL, "%s: bad file type", __func__ );
+ return Q_ERR_NOSYS;
}
-
- return INVALID_LENGTH;
}
/*
@@ -411,37 +388,36 @@ size_t FS_GetFileLength( fileHandle_t f ) {
FS_Tell
============
*/
-size_t FS_Tell( fileHandle_t f ) {
- file_t *file = FS_FileForHandle( f );
- size_t pos = INVALID_LENGTH;
+ssize_t FS_Tell( qhandle_t f ) {
+ file_t *file = file_for_handle( f );
long ret;
switch( file->type ) {
case FS_REAL:
ret = ftell( file->fp );
- if( ret != -1 ) {
- pos = (size_t)ret;
+ if( ret == -1 ) {
+ return Q_ERR(errno);
}
- break;
+ return ret;
case FS_PAK:
ret = ftell( file->fp );
- if( ret != -1 && ret >= file->entry->filepos ) {
- pos = (size_t)ret;
+ if( ret == -1 ) {
+ return Q_ERR(errno);
}
- break;
+ if( ret < file->entry->filepos ||
+ ret > file->entry->filepos +
+ file->entry->filelen )
+ {
+ return Q_ERR_SPIPE;
+ }
+ return ret;
#if USE_ZLIB
case FS_ZIP:
- pos = tell_zip_file( file );
- break;
- case FS_GZ:
- pos = INVALID_LENGTH;
- break;
+ return tell_zip_file( file );
#endif
default:
- Com_Error( ERR_FATAL, "%s: bad file type", __func__ );
+ return Q_ERR_NOSYS;
}
-
- return pos;
}
/*
@@ -449,34 +425,30 @@ size_t FS_Tell( fileHandle_t f ) {
FS_Seek
============
*/
-qboolean FS_Seek( fileHandle_t f, size_t offset ) {
- file_t *file = FS_FileForHandle( f );
+qerror_t FS_Seek( qhandle_t f, size_t offset ) {
+ file_t *file = file_for_handle( f );
if( offset > LONG_MAX ) {
- return qfalse;
+ return Q_ERR_INVAL;
}
switch( file->type ) {
case FS_REAL:
- case FS_PAK:
+ //case FS_PAK:
if( fseek( file->fp, (long)offset, SEEK_CUR ) == -1 ) {
- return qfalse;
+ return Q_ERR(errno);
}
- break;
+ return Q_ERR_SUCCESS;
#if USE_ZLIB
- case FS_ZIP:
- return qfalse;
case FS_GZ:
if( gzseek( file->zfp, (long)offset, SEEK_CUR ) == -1 ) {
- return qfalse;
+ return Q_ERR(errno);
}
- break;
+ return Q_ERR_SUCCESS;
#endif
default:
- Com_Error( ERR_FATAL, "%s: bad file type", __func__ );
+ return Q_ERR_NOSYS;
}
-
- return qtrue;
}
@@ -488,21 +460,27 @@ Creates any directories needed to store the given filename.
Expects a fully qualified quake path (i.e. with / separators).
============
*/
-void FS_CreatePath( char *path ) {
+qerror_t FS_CreatePath( char *path ) {
char *ofs;
+ int ret;
if( !*path ) {
- return;
+ return Q_ERR_INVAL;
}
for( ofs = path + 1; *ofs; ofs++ ) {
if( *ofs == '/' ) {
// create the directory
*ofs = 0;
- Q_mkdir( path );
+ ret = Q_mkdir( path );
*ofs = '/';
+ if( ret == -1 && errno != EEXIST ) {
+ return Q_ERR(errno);
+ }
}
}
+
+ return Q_ERR_SUCCESS;
}
/*
@@ -513,20 +491,20 @@ Turns FS_REAL file into FS_GZIP by reopening it through GZIP.
File position is reset to the beginning of file.
============
*/
-qboolean FS_FilterFile( fileHandle_t f ) {
+qerror_t FS_FilterFile( qhandle_t f ) {
#if USE_ZLIB
- file_t *file = FS_FileForHandle( f );
- int mode;
+ file_t *file = file_for_handle( f );
+ unsigned mode;
char *modeStr;
void *zfp;
switch( file->type ) {
case FS_GZ:
- return qtrue;
+ return Q_ERR_SUCCESS;
case FS_REAL:
break;
default:
- return qfalse;
+ return Q_ERR_NOSYS;
}
mode = file->mode & FS_MODE_MASK;
@@ -542,21 +520,19 @@ qboolean FS_FilterFile( fileHandle_t f ) {
}
if( fseek( file->fp, 0, SEEK_SET ) == -1 ) {
- FS_DPrintf( "%s: couldn't seek to the beginning of file\n", __func__ );
- return qfalse;
+ return Q_ERR(errno);
}
zfp = gzdopen( fileno( file->fp ), modeStr );
if( !zfp ) {
- FS_DPrintf( "%s: couldn't reopen file through gzip\n", __func__ );
- return qfalse;
+ return Q_ERR_FAILURE;
}
file->zfp = zfp;
file->type = FS_GZ;
- return qtrue;
+ return Q_ERR_SUCCESS;
#else
- return qfalse;
+ return Q_ERR_NOSYS;
#endif
}
@@ -566,8 +542,8 @@ qboolean FS_FilterFile( fileHandle_t f ) {
FS_FCloseFile
==============
*/
-void FS_FCloseFile( fileHandle_t f ) {
- file_t *file = FS_FileForHandle( f );
+void FS_FCloseFile( qhandle_t f ) {
+ file_t *file = file_for_handle( f );
FS_DPrintf( "%s: %u\n", __func__, f );
@@ -599,22 +575,18 @@ void FS_FCloseFile( fileHandle_t f ) {
memset( file, 0, sizeof( *file ) );
}
-/*
-============
-FS_FOpenFileWrite
-============
-*/
-static size_t FS_FOpenFileWrite( file_t *file, const char *name ) {
+static ssize_t open_file_write( file_t *file, const char *name ) {
char fullpath[MAX_OSPATH];
FILE *fp;
char *modeStr;
unsigned mode;
size_t len;
- long ret;
+ long pos;
+ qerror_t ret;
- if( !FS_ValidatePath( name ) ) {
- FS_DPrintf( "%s: refusing invalid path: %s\n", __func__, name );
- return INVALID_LENGTH;
+ ret = validate_path( name );
+ if( ret ) {
+ return ret;
}
if( ( file->mode & FS_PATH_MASK ) == FS_PATH_BASE ) {
@@ -630,8 +602,7 @@ static size_t FS_FOpenFileWrite( file_t *file, const char *name ) {
fs_gamedir, "/", name, NULL );
}
if( len >= sizeof( fullpath ) ) {
- FS_DPrintf( "%s: refusing oversize path: %s\n", __func__, name );
- return INVALID_LENGTH;
+ return Q_ERR_NAMETOOLONG;
}
mode = file->mode & FS_MODE_MASK;
@@ -640,7 +611,15 @@ static size_t FS_FOpenFileWrite( file_t *file, const char *name ) {
modeStr = "ab";
break;
case FS_MODE_WRITE:
- modeStr = "wb";
+ if( file->mode & FS_FLAG_EXCL ) {
+#ifdef _GNU_SOURCE
+ modeStr = "wxb";
+#else
+ return Q_ERR_INVAL; // TODO
+#endif
+ } else {
+ modeStr = "wb";
+ }
break;
case FS_MODE_RDWR:
// this mode is only used by client downloading code
@@ -649,40 +628,38 @@ static size_t FS_FOpenFileWrite( file_t *file, const char *name ) {
modeStr = "r+b";
break;
default:
- Com_Error( ERR_FATAL, "%s: %s: invalid mode mask", __func__, name );
+ return Q_ERR_INVAL;
}
- FS_CreatePath( fullpath );
+ ret = FS_CreatePath( fullpath );
+ if( ret ) {
+ return ret;
+ }
fp = fopen( fullpath, modeStr );
if( !fp ) {
- FS_DPrintf( "%s: %s: couldn't open\n", __func__, fullpath );
- return INVALID_LENGTH;
+ return Q_ERR(errno);
}
#ifdef __unix__
// check if this is a regular file
- if( !Sys_GetFileInfo( fp, NULL ) ) {
- FS_DPrintf( "%s: %s: couldn't get info\n", __func__, fullpath );
- goto fail;
+ ret = Sys_GetFileInfo( fp, NULL );
+ if( ret ) {
+ goto fail2;
}
#endif
if( mode == FS_MODE_RDWR ) {
// seek to the end of file for appending
if( fseek( fp, 0, SEEK_END ) == -1 ) {
- FS_DPrintf( "%s: %s: couldn't seek to the end of file\n",
- __func__, fullpath );
- goto fail;
+ goto fail1;
}
}
// return current position (non-zero for appending modes)
- ret = ftell( fp );
- if( ret == -1 ) {
- FS_DPrintf( "%s: %s: couldn't get current position\n",
- __func__, fullpath );
- goto fail;
+ pos = ftell( fp );
+ if( pos == -1 ) {
+ goto fail1;
}
FS_DPrintf( "%s: %s: succeeded\n", __func__, fullpath );
@@ -692,29 +669,43 @@ static size_t FS_FOpenFileWrite( file_t *file, const char *name ) {
file->length = 0;
file->unique = qtrue;
- return (size_t)ret;
+ return pos;
-fail:
+fail1:
+ ret = Q_ERR(errno);
+fail2:
fclose( fp );
- return INVALID_LENGTH;
+ return ret;
+}
+
+// functions that report errors for partial reads/writes
+static inline ssize_t read_block( void *buf, size_t size, FILE *fp ) {
+ size_t result = fread( buf, 1, size, fp );
+ return result == size ? result : ferror(fp) ? Q_ERR(errno) : result;
+}
+
+static inline ssize_t write_block( void *buf, size_t size, FILE *fp ) {
+ size_t result = fwrite( buf, 1, size, fp );
+ return result == size ? result : ferror(fp) ? Q_ERR(errno) : result;
}
#if USE_ZLIB
-static size_t check_header_coherency( FILE *fp, packfile_t *entry ) {
+static qerror_t check_header_coherency( FILE *fp, packfile_t *entry ) {
unsigned flags, comp_mtd;
size_t comp_len, file_len;
size_t name_size, xtra_size;
byte header[ZIP_SIZELOCALHEADER];
+ size_t ofs;
if( fseek( fp, (long)entry->filepos, SEEK_SET ) == -1 )
- return 0;
+ return Q_ERR(errno);
if( fread( header, 1, sizeof( header ), fp ) != sizeof( header ) )
- return 0;
+ return ferror( fp ) ? Q_ERR(errno) : Q_ERR_UNEXPECTED_EOF;
// check the magic
if( LittleLongMem( &header[0] ) != ZIP_LOCALHEADERMAGIC )
- return 0;
+ return Q_ERR_NOT_COHERENT;
flags = LittleShortMem( &header[6] );
comp_mtd = LittleShortMem( &header[8] );
@@ -724,18 +715,25 @@ static size_t check_header_coherency( FILE *fp, packfile_t *entry ) {
xtra_size = LittleShortMem( &header[28] );
if( comp_mtd != entry->compmtd )
- return 0;
+ return Q_ERR_NOT_COHERENT;
// bit 3 tells that file lengths were not known
// at the time local header was written, so don't check them
if( ( flags & 8 ) == 0 ) {
if( comp_len != entry->complen )
- return 0;
+ return Q_ERR_NOT_COHERENT;
if( file_len != entry->filelen )
- return 0;
+ return Q_ERR_NOT_COHERENT;
+ }
+
+ ofs = ZIP_SIZELOCALHEADER + name_size + xtra_size;
+ if( entry->filepos > LONG_MAX - ofs ) {
+ return Q_ERR_SPIPE;
}
- return ZIP_SIZELOCALHEADER + name_size + xtra_size;
+ entry->filepos += ofs;
+ entry->coherent = qtrue;
+ return Q_ERR_SUCCESS;
}
static voidpf FS_zalloc OF(( voidpf opaque, uInt items, uInt size )) {
@@ -778,6 +776,7 @@ static void open_zip_file( file_t *file ) {
s->rest_in = entry->complen;
s->rest_out = entry->filelen;
+ s->error = Q_ERR_SUCCESS;
file->zfp = s;
}
@@ -792,7 +791,7 @@ static void close_zip_file( file_t *file ) {
fclose( file->fp );
}
-static size_t tell_zip_file( file_t *file ) {
+static ssize_t tell_zip_file( file_t *file ) {
zipstream_t *s = file->zfp;
if( !file->entry->compmtd ) {
@@ -801,12 +800,18 @@ static size_t tell_zip_file( file_t *file ) {
return s->stream.total_out;
}
-static size_t read_zip_file( file_t *file, void *buf, size_t len ) {
+static ssize_t read_zip_file( file_t *file, void *buf, size_t len ) {
zipstream_t *s = file->zfp;
z_streamp z = &s->stream;
- size_t block, result;
+ size_t block;
+ ssize_t result;
int ret;
+ // can't continue after error
+ if( s->error ) {
+ return s->error;
+ }
+
if( len > s->rest_out ) {
len = s->rest_out;
}
@@ -815,15 +820,19 @@ static size_t read_zip_file( file_t *file, void *buf, size_t len ) {
if( len > s->rest_in ) {
len = s->rest_in;
}
+ if( !len ) {
+ return 0;
+ }
- result = fread( buf, 1, len, file->fp );
- if( result != len ) {
- Com_EPrintf( "%s: fread() failed\n", __func__ );
+ result = read_block( buf, len, file->fp );
+ if( result <= 0 ) {
+ s->error = result ? result : Q_ERR_UNEXPECTED_EOF;
+ return s->error;
}
s->rest_in -= result;
s->rest_out -= result;
- return len;
+ return result;
}
z->next_out = buf;
@@ -841,25 +850,25 @@ static size_t read_zip_file( file_t *file, void *buf, size_t len ) {
block = s->rest_in;
}
- result = fread( s->buffer, 1, block, file->fp );
- if( result != block ) {
- Com_EPrintf( "%s: fread() failed\n", __func__ );
- }
- if( !result ) {
- break;
+ result = read_block( s->buffer, block, file->fp );
+ if( result <= 0 ) {
+ s->error = result ? result : Q_ERR_UNEXPECTED_EOF;
+ return s->error;
}
s->rest_in -= result;
z->next_in = s->buffer;
z->avail_in = result;
}
+ //if(z->total_out>1024*128)return Q_ERR(EIO);
ret = inflate( z, Z_SYNC_FLUSH );
if( ret == Z_STREAM_END ) {
break;
}
if( ret != Z_OK ) {
- Com_EPrintf( "%s: inflate() failed: %s\n", __func__, z->msg );
+ s->error = Q_ERR_INFLATE_FAILED;
+ //Com_Printf("%s\n",z->msg );
break;
}
}
@@ -867,44 +876,41 @@ static size_t read_zip_file( file_t *file, void *buf, size_t len ) {
len -= z->avail_out;
s->rest_out -= len;
+ if( s->error && len == 0 ) {
+ return s->error;
+ }
+
return len;
}
#endif
// open a new file on the pakfile
-static size_t FS_FOpenFromPak( file_t *file, pack_t *pack, packfile_t *entry, qboolean unique ) {
+static ssize_t open_from_pak( file_t *file, pack_t *pack, packfile_t *entry, qboolean unique ) {
FILE *fp;
+ int ret;
if( unique ) {
fp = fopen( pack->filename, "rb" );
if( !fp ) {
- Com_EPrintf( "%s: couldn't reopen %s\n",
- __func__, pack->filename );
- return INVALID_LENGTH;
+ return Q_ERR(errno);
}
} else {
fp = pack->fp;
+ clearerr( fp );
}
#if USE_ZLIB
if( pack->type == FS_ZIP && !entry->coherent ) {
- size_t ofs = check_header_coherency( fp, entry );
-
- if( !ofs || entry->filepos > LONG_MAX - ofs ) {
- Com_EPrintf( "%s: coherency check failed on %s\n",
- __func__, pack->filename );
+ ret = check_header_coherency( fp, entry );
+ if( ret ) {
goto fail;
}
-
- entry->filepos += ofs;
- entry->coherent = qtrue;
}
#endif
if( fseek( fp, (long)entry->filepos, SEEK_SET ) == -1 ) {
- Com_EPrintf( "%s: couldn't seek into %s\n",
- __func__, pack->filename );
+ ret = Q_ERR(errno);
goto fail;
}
@@ -929,29 +935,19 @@ static size_t FS_FOpenFromPak( file_t *file, pack_t *pack, packfile_t *entry, qb
FS_DPrintf( "%s: %s/%s: succeeded\n",
__func__, pack->filename, entry->name );
- fs_fileFromPak = qtrue;
-
- return file->length;
+ return entry->filelen;
fail:
if( unique ) {
fclose( fp );
}
- return INVALID_LENGTH;
+ return ret;
}
-/*
-===========
-FS_FOpenFileRead
-
-Finds the file in the search path.
-Fills file_t and returns file length.
-In case of GZIP files, returns *raw* (compressed) length!
-Used for streaming data out of either a pak file or
-a seperate file.
-===========
-*/
-static size_t FS_FOpenFileRead( file_t *file, const char *name, qboolean unique ) {
+// Finds the file in the search path.
+// Fills file_t and returns file length.
+// Used for streaming data out of either a pak file or a seperate file.
+static ssize_t open_file_read( file_t *file, const char *name, qboolean unique ) {
char fullpath[MAX_OSPATH];
searchpath_t *search;
pack_t *pak;
@@ -959,10 +955,9 @@ static size_t FS_FOpenFileRead( file_t *file, const char *name, qboolean unique
packfile_t *entry;
FILE *fp;
file_info_t info;
- int valid = -1;
+ int ret = Q_ERR_SUCCESS, valid = -1;
size_t len;
- fs_fileFromPak = qfalse;
#ifdef _DEBUG
fs_count_read++;
#endif
@@ -993,12 +988,7 @@ static size_t FS_FOpenFileRead( file_t *file, const char *name, qboolean unique
#endif
if( !FS_pathcmp( entry->name, name ) ) {
// found it!
- len = FS_FOpenFromPak( file, pak, entry, unique );
- if( len == INVALID_LENGTH ) {
- // failed to open pak, continue to search
- break;
- }
- return len;
+ return open_from_pak( file, pak, entry, unique );
}
}
} else {
@@ -1006,9 +996,8 @@ static size_t FS_FOpenFileRead( file_t *file, const char *name, qboolean unique
continue;
}
if( valid == -1 ) {
- if( !FS_ValidatePath( name ) ) {
- FS_DPrintf( "%s: refusing invalid path: %s\n",
- __func__, name );
+ ret = validate_path( name );
+ if( ret ) {
valid = 0;
}
}
@@ -1019,9 +1008,7 @@ static size_t FS_FOpenFileRead( file_t *file, const char *name, qboolean unique
len = Q_concat( fullpath, sizeof( fullpath ),
search->filename, "/", name, NULL );
if( len >= sizeof( fullpath ) ) {
- FS_DPrintf( "%s: refusing oversize path: %s\n",
- __func__, name );
- continue;
+ return Q_ERR_NAMETOOLONG;
}
#ifdef _DEBUG
@@ -1029,14 +1016,16 @@ static size_t FS_FOpenFileRead( file_t *file, const char *name, qboolean unique
#endif
fp = fopen( fullpath, "rb" );
if( !fp ) {
- continue;
+ if( errno == ENOENT ) {
+ continue;
+ }
+ return Q_ERR(errno);
}
- if( !Sys_GetFileInfo( fp, &info ) ) {
- FS_DPrintf( "%s: %s: couldn't get info\n",
- __func__, fullpath );
+ ret = Sys_GetFileInfo( fp, &info );
+ if( ret ) {
fclose( fp );
- continue;
+ return ret;
}
file->fp = fp;
@@ -1046,25 +1035,15 @@ static size_t FS_FOpenFileRead( file_t *file, const char *name, qboolean unique
FS_DPrintf( "%s: %s: succeeded\n", __func__, fullpath );
- return file->length;
+ return info.size;
}
}
- FS_DPrintf( "%s: %s: not found\n", __func__, name );
+ FS_DPrintf( "%s: %s: failed\n", __func__, name );
- return INVALID_LENGTH;
-}
-
-/*
-=================
-FS_LastFileFromPak
-=================
-*/
-qboolean FS_LastFileFromPak( void ) {
- return fs_fileFromPak;
+ return ret == Q_ERR_SUCCESS ? Q_ERR_NOENT : ret;
}
-
/*
=================
FS_ReadFile
@@ -1072,10 +1051,15 @@ FS_ReadFile
Properly handles partial reads
=================
*/
-size_t FS_Read( void *buffer, size_t len, fileHandle_t f ) {
- size_t block, remaining = len, read = 0;
+ssize_t FS_Read( void *buffer, size_t len, qhandle_t f ) {
+ size_t block, remaining = len;
+ ssize_t read = 0;
byte *buf = (byte *)buffer;
- file_t *file = FS_FileForHandle( f );
+ file_t *file = file_for_handle( f );
+
+ if( len > SSIZE_MAX ) {
+ return Q_ERR_INVAL;
+ }
// read in chunks for progress bar
while( remaining ) {
@@ -1085,14 +1069,23 @@ size_t FS_Read( void *buffer, size_t len, fileHandle_t f ) {
switch( file->type ) {
case FS_REAL:
case FS_PAK:
- read = fread( buf, 1, block, file->fp );
+ read = read_block( buf, block, file->fp );
+ if( read < 0 ) {
+ return read;
+ }
break;
#if USE_ZLIB
case FS_GZ:
read = gzread( file->zfp, buf, block );
+ if( read < 0 ) {
+ return Q_ERR_INFLATE_FAILED;
+ }
break;
case FS_ZIP:
read = read_zip_file( file, buf, block );
+ if( read < 0 ) {
+ return read;
+ }
break;
#endif
default:
@@ -1101,9 +1094,6 @@ size_t FS_Read( void *buffer, size_t len, fileHandle_t f ) {
if( read == 0 ) {
return len - remaining;
}
- if( read > block ) {
- Com_Error( ERR_FATAL, "FS_Read: %"PRIz" bytes read", read );
- }
remaining -= read;
buf += read;
@@ -1112,18 +1102,18 @@ size_t FS_Read( void *buffer, size_t len, fileHandle_t f ) {
return len;
}
-size_t FS_ReadLine( fileHandle_t f, char *buffer, int size ) {
- file_t *file = FS_FileForHandle( f );
+ssize_t FS_ReadLine( qhandle_t f, char *buffer, size_t size ) {
+ file_t *file = file_for_handle( f );
char *s;
size_t len;
if( file->type != FS_REAL ) {
- return 0;
+ return Q_ERR_NOSYS;
}
do {
s = fgets( buffer, size, file->fp );
if( !s ) {
- return 0;
+ return ferror( file->fp ) ? Q_ERR(errno) : 0;
}
len = strlen( s );
} while( len < 2 );
@@ -1132,8 +1122,8 @@ size_t FS_ReadLine( fileHandle_t f, char *buffer, int size ) {
return len - 1;
}
-void FS_Flush( fileHandle_t f ) {
- file_t *file = FS_FileForHandle( f );
+void FS_Flush( qhandle_t f ) {
+ file_t *file = file_for_handle( f );
switch( file->type ) {
case FS_REAL:
@@ -1156,10 +1146,15 @@ FS_Write
Properly handles partial writes
=================
*/
-size_t FS_Write( const void *buffer, size_t len, fileHandle_t f ) {
- size_t block, remaining = len, write = 0;
+ssize_t FS_Write( const void *buffer, size_t len, qhandle_t f ) {
+ size_t block, remaining = len;
+ ssize_t write = 0;
byte *buf = (byte *)buffer;
- file_t *file = FS_FileForHandle( f );
+ file_t *file = file_for_handle( f );
+
+ if( len > SSIZE_MAX ) {
+ return Q_ERR_INVAL;
+ }
// read in chunks for progress bar
while( remaining ) {
@@ -1168,23 +1163,25 @@ size_t FS_Write( const void *buffer, size_t len, fileHandle_t f ) {
block = MAX_WRITE;
switch( file->type ) {
case FS_REAL:
- write = fwrite( buf, 1, block, file->fp );
+ write = write_block( buf, block, file->fp );
+ if( write < 0 ) {
+ return write;
+ }
break;
#if USE_ZLIB
case FS_GZ:
write = gzwrite( file->zfp, buf, block );
+ if( write < 0 ) {
+ return Q_ERR_DEFLATE_FAILED;
+ }
break;
#endif
default:
Com_Error( ERR_FATAL, "%s: bad file type", __func__ );
- break;
}
if( write == 0 ) {
return len - remaining;
}
- if( write > block ) {
- Com_Error( ERR_FATAL, "FS_Write: %"PRIz" bytes written", write );
- }
remaining -= write;
buf += write;
@@ -1208,7 +1205,7 @@ size_t FS_Write( const void *buffer, size_t len, fileHandle_t f ) {
return len;
}
-static char *FS_ExpandLinks( const char *filename ) {
+static char *expand_links( const char *filename ) {
static char buffer[MAX_OSPATH];
symlink_t *link;
size_t len;
@@ -1239,10 +1236,10 @@ static char *FS_ExpandLinks( const char *filename ) {
FS_FOpenFile
============
*/
-size_t FS_FOpenFile( const char *name, fileHandle_t *f, int mode ) {
+ssize_t FS_FOpenFile( const char *name, qhandle_t *f, unsigned mode ) {
file_t *file;
- fileHandle_t handle;
- size_t ret = INVALID_LENGTH;
+ qhandle_t handle;
+ ssize_t ret;
if( !name || !f ) {
Com_Error( ERR_FATAL, "%s: NULL", __func__ );
@@ -1251,7 +1248,7 @@ size_t FS_FOpenFile( const char *name, fileHandle_t *f, int mode ) {
*f = 0;
if( !fs_searchpaths ) {
- return ret; // not yet initialized
+ return Q_ERR_AGAIN; // not yet initialized
}
if( *name == '/' ) {
@@ -1259,70 +1256,93 @@ size_t FS_FOpenFile( const char *name, fileHandle_t *f, int mode ) {
}
if( ( mode & FS_MODE_MASK ) == FS_MODE_READ ) {
- name = FS_ExpandLinks( name );
+ name = expand_links( name );
}
// allocate new file handle
- file = FS_AllocHandle( &handle );
+ file = alloc_handle( &handle );
if( !file ) {
- Com_EPrintf( "%s: no free file handles\n", __func__ );
- return ret;
+ return Q_ERR_NFILE;
}
+
file->mode = mode;
- mode &= FS_MODE_MASK;
- switch( mode ) {
- case FS_MODE_READ:
- ret = FS_FOpenFileRead( file, name, qtrue );
- break;
- case FS_MODE_WRITE:
- case FS_MODE_APPEND:
- case FS_MODE_RDWR:
- ret = FS_FOpenFileWrite( file, name );
- break;
- default:
- Com_Error( ERR_FATAL, "%s: illegal mode: %u", __func__, mode );
- break;
+ if( ( mode & FS_MODE_MASK ) == FS_MODE_READ ) {
+ ret = open_file_read( file, name, qtrue );
+ } else {
+ ret = open_file_write( file, name );
}
- // if succeeded, store file handle
- if( ret != -1 ) {
+ if( ret >= 0 ) {
*f = handle;
}
return ret;
}
-#if USE_LOADBUF
+/*
+============
+FS_EasyOpenFile
+
+Helper function for various console commands. Concatenates
+the arguments, checks for path buffer overflow, and attempts
+to open the file, printing an error message in case of failure.
+============
+*/
+qhandle_t FS_EasyOpenFile( char *buf, size_t size, unsigned mode,
+ const char *dir, const char *name, const char *ext )
+{
+ size_t len;
+ qhandle_t f;
+ qerror_t ret;
+ char *gz = NULL;
+
+ if( mode & FS_FLAG_GZIP ) {
+ gz = ".gz";
+ }
+
+ // TODO: don't append the extension if name already has it
+
+ len = Q_concat( buf, size, dir, name, ext, gz, NULL );
+ if( len >= size ) {
+ ret = Q_ERR_NAMETOOLONG;
+ goto fail1;
+ }
-#define MAX_LOAD_BUFFER 0x100000 // 1 MiB
+ ret = FS_FOpenFile( buf, &f, mode );
+ if( !f ) {
+ goto fail1;
+ }
-// static buffer for small, stacked file loads and temp allocations
-// the last allocation may be easily undone
-static byte loadBuffer[MAX_LOAD_BUFFER];
-static byte *loadLast;
-static size_t loadSaved;
-static size_t loadInuse;
-static int loadStack;
+ if( mode & FS_FLAG_GZIP ) {
+ ret = FS_FilterFile( f );
+ if( ret ) {
+ goto fail2;
+ }
+ }
-// for statistics
-static int loadCount;
-static int loadCountStatic;
+ return f;
-#endif // USE_LOADBUF
+fail2:
+ FS_FCloseFile( f );
+fail1:
+ Com_EPrintf( "Couldn't open %s for writing: %s\n", buf, Q_ErrorString( ret ) );
+ return 0;
+}
/*
============
FS_LoadFile
+opens non-unique file handle as an optimization
a NULL buffer will just return the file length without loading
============
*/
-size_t FS_LoadFileEx( const char *path, void **buffer, int flags, memtag_t tag ) {
+ssize_t FS_LoadFileEx( const char *path, void **buffer, unsigned flags ) {
file_t *file;
- fileHandle_t f;
+ qhandle_t f;
byte *buf;
- size_t len;
+ ssize_t len, read;
if( !path ) {
Com_Error( ERR_FATAL, "%s: NULL", __func__ );
@@ -1333,117 +1353,93 @@ size_t FS_LoadFileEx( const char *path, void **buffer, int flags, memtag_t tag )
}
if( !fs_searchpaths ) {
- return INVALID_LENGTH; // not yet initialized
+ return Q_ERR_AGAIN; // not yet initialized
}
if( *path == '/' ) {
path++;
}
- path = FS_ExpandLinks( path );
+ path = expand_links( path );
// allocate new file handle
- file = FS_AllocHandle( &f );
+ file = alloc_handle( &f );
if( !file ) {
- Com_EPrintf( "%s: no free file handles\n", __func__ );
- return INVALID_LENGTH;
+ return Q_ERR_NOENT;
}
file->mode = ( flags & ~FS_MODE_MASK ) | FS_MODE_READ;
// look for it in the filesystem or pack files
- len = FS_FOpenFileRead( file, path, qfalse );
- if( len == INVALID_LENGTH ) {
+ len = open_file_read( file, path, qfalse );
+ if( len < 0 ) {
return len;
}
// NULL buffer just checks for file existence
- if( buffer ) {
- if( len > MAX_LOADFILE ) {
- Com_EPrintf( "%s: %s is too large to be loaded: %"PRIz" bytes\n",
- __func__, path, len );
- len = INVALID_LENGTH;
- goto fail;
- }
- if( tag == TAG_FREE ) {
- buf = FS_AllocTempMem( len + 1 );
- } else {
- buf = Z_TagMalloc( len + 1, tag );
- }
- if( FS_Read( buf, len, f ) == len ) {
- *buffer = buf;
- buf[len] = 0;
- } else {
- Com_EPrintf( "%s: error reading file: %s\n", __func__, path );
- if( tag == TAG_FREE ) {
- FS_FreeFile( buf );
- } else {
- Z_Free( buf );
- }
- len = INVALID_LENGTH;
- }
+ if( !buffer ) {
+ goto done;
}
-fail:
+ // sanity check file size
+ if( len > MAX_LOADFILE ) {
+ len = Q_ERR_FBIG;
+ goto done;
+ }
+
+ // allocate chunk of memory, +1 for NUL
+ buf = FS_Malloc( len + 1 );
+
+ // read entire file
+ read = FS_Read( buf, len, f );
+ if( read != len ) {
+ len = read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
+ Z_Free( buf );
+ goto done;
+ }
+
+ *buffer = buf;
+ buf[len] = 0;
+
+done:
FS_FCloseFile( f );
return len;
}
-size_t FS_LoadFile( const char *path, void **buffer ) {
- return FS_LoadFileEx( path, buffer, 0, TAG_FREE );
+ssize_t FS_LoadFile( const char *path, void **buffer ) {
+ return FS_LoadFileEx( path, buffer, 0 );
}
-void *FS_AllocTempMem( size_t length ) {
- byte *buf;
+void *FS_AllocTempMem( size_t len ) {
+ return FS_Malloc( len );
+}
-#if USE_LOADBUF
- if( loadInuse + length <= MAX_LOAD_BUFFER ) {
- buf = &loadBuffer[loadInuse];
- loadLast = buf;
- loadSaved = loadInuse;
- loadInuse += length;
- loadInuse = ( loadInuse + 31 ) & ~31;
- loadStack++;
- loadCountStatic++;
- } else
-#endif
- {
- // Com_Printf(S_COLOR_MAGENTA"alloc %d\n",length);
- buf = FS_Malloc( length );
-#if USE_LOADBUF
- loadCount++;
-#endif
- }
- return buf;
+void FS_FreeFile( void *buf ) {
+ Z_Free( buf );
}
/*
-=============
-FS_FreeFile
-=============
+================
+FS_WriteFile
+================
*/
-void FS_FreeFile( void *buffer ) {
- if( !buffer ) {
- Com_Error( ERR_FATAL, "%s: NULL", __func__ );
+qerror_t FS_WriteFile( const char *path, const void *data, size_t len ) {
+ qhandle_t f;
+ ssize_t write;
+ qerror_t ret;
+
+ ret = FS_FOpenFile( path, &f, FS_MODE_WRITE );
+ if( !f ) {
+ return ret;
}
-#if USE_LOADBUF
- if( ( byte * )buffer >= loadBuffer && ( byte * )buffer < loadBuffer + MAX_LOAD_BUFFER ) {
- if( loadStack == 0 ) {
- Com_Error( ERR_FATAL, "%s: empty load stack", __func__ );
- }
- loadStack--;
- if( loadStack == 0 ) {
- loadInuse = 0;
- // Com_Printf(S_COLOR_MAGENTA"clear\n");
- } else if( buffer == loadLast ) {
- loadInuse = loadSaved;
- // Com_Printf(S_COLOR_MAGENTA"partial\n");
- }
- } else
-#endif
- {
- Z_Free( buffer );
+
+ write = FS_Write( data, len, f );
+ if( write != len ) {
+ ret = write < 0 ? write : Q_ERR_FAILURE;
}
+
+ FS_FCloseFile( f );
+ return ret;
}
#if USE_CLIENT
@@ -1453,41 +1449,41 @@ void FS_FreeFile( void *buffer ) {
FS_RenameFile
================
*/
-qboolean FS_RenameFile( const char *from, const char *to ) {
+qerror_t FS_RenameFile( const char *from, const char *to ) {
char frompath[MAX_OSPATH];
char topath[MAX_OSPATH];
size_t len;
+ int ret;
if( *from == '/' ) {
from++;
}
- if( !FS_ValidatePath( from ) ) {
- FS_DPrintf( "%s: refusing invalid source path: %s\n", __func__, from );
- return qfalse;
+ ret = validate_path( from );
+ if( ret ) {
+ return ret;
}
len = Q_concat( frompath, sizeof( frompath ), fs_gamedir, "/", from, NULL );
if( len >= sizeof( frompath ) ) {
- FS_DPrintf( "%s: refusing oversize source path: %s\n", __func__, frompath );
- return qfalse;
+ return Q_ERR_NAMETOOLONG;
}
if( *to == '/' ) {
to++;
}
- if( !FS_ValidatePath( to ) ) {
- FS_DPrintf( "%s: refusing invalid destination path: %s\n", __func__, to );
- return qfalse;
+ ret = validate_path( to );
+ if( ret ) {
+ return ret;
}
len = Q_concat( topath, sizeof( topath ), fs_gamedir, "/", to, NULL );
if( len >= sizeof( topath ) ) {
- FS_DPrintf( "%s: refusing oversize destination path: %s\n", __func__, topath );
- return qfalse;
+ return Q_ERR_NAMETOOLONG;
}
if( rename( frompath, topath ) ) {
- return qfalse;
+ return Q_ERR(errno);
}
- return qtrue;
+
+ return Q_ERR_SUCCESS;
}
#endif // USE_CLIENT
@@ -1497,16 +1493,20 @@ qboolean FS_RenameFile( const char *from, const char *to ) {
FS_FPrintf
================
*/
-void FS_FPrintf( fileHandle_t f, const char *format, ... ) {
+ssize_t FS_FPrintf( qhandle_t f, const char *format, ... ) {
va_list argptr;
char string[MAXPRINTMSG];
size_t len;
va_start( argptr, format );
- len = Q_vscnprintf( string, sizeof( string ), format, argptr );
+ len = Q_vsnprintf( string, sizeof( string ), format, argptr );
va_end( argptr );
- FS_Write( string, len, f );
+ if( len >= sizeof( string ) ) {
+ return Q_ERR_STRING_TRUNCATED;
+ }
+
+ return FS_Write( string, len, f );
}
// references pack_t instance
@@ -1560,17 +1560,9 @@ static pack_t *pack_alloc( FILE *fp, filetype_t type, const char *name,
return pack;
}
-/*
-=================
-FS_LoadPakFile
-
-Takes an explicit (not game tree related) path to a pak file.
-
-Loads the header and directory, adding the files at the beginning
-of the list so they override previous pack files.
-=================
-*/
-static pack_t *FS_LoadPakFile( const char *packfile ) {
+// Loads the header and directory, adding the files at the beginning
+// of the list so they override previous pack files.
+static pack_t *load_pak_file( const char *packfile ) {
dpackheader_t header;
int i;
packfile_t *file;
@@ -1586,7 +1578,7 @@ static pack_t *FS_LoadPakFile( const char *packfile ) {
fp = fopen( packfile, "rb" );
if( !fp ) {
- Com_Printf( "Couldn't open %s\n", packfile );
+ Com_Printf( "Couldn't open %s: %s\n", packfile, strerror( errno ) );
return NULL;
}
@@ -1672,11 +1664,6 @@ fail:
}
#if USE_ZLIB
-/*
-=================
-FS_LoadZipFile
-=================
-*/
// Locate the central directory of a zipfile (at the end, just before the global comment)
static size_t search_central_header( FILE *fp ) {
@@ -1799,7 +1786,7 @@ skip:
return ZIP_SIZECENTRALDIRITEM + name_size + xtra_size + comm_size;
}
-static pack_t *FS_LoadZipFile( const char *packfile ) {
+static pack_t *load_zip_file( const char *packfile ) {
int i;
packfile_t *file;
char *name;
@@ -1815,7 +1802,7 @@ static pack_t *FS_LoadZipFile( const char *packfile ) {
fp = fopen( packfile, "rb" );
if( !fp ) {
- Com_Printf( "Couldn't open %s\n", packfile );
+ Com_Printf( "Couldn't open %s: %s\n", packfile, strerror( errno ) );
return NULL;
}
@@ -1964,21 +1951,23 @@ alphacmp:
return FS_strcmp( s1, s2 );
}
-static void FS_LoadPackFiles( int mode, const char *extension, pack_t *(loadfunc)( const char * ) ) {
+static void load_pack_files( unsigned mode, const char *ext, pack_t *(loadfunc)( const char * ) ) {
int i;
searchpath_t *search;
pack_t *pack;
void **list;
- int numFiles;
+ int num_files;
char path[MAX_OSPATH];
size_t len;
- list = Sys_ListFiles( fs_gamedir, extension, FS_SEARCH_NOSORT, 0, &numFiles );
+ list = Sys_ListFiles( fs_gamedir, ext, FS_SEARCH_NOSORT, 0, &num_files );
if( !list ) {
return;
}
- qsort( list, numFiles, sizeof( list[0] ), pakcmp );
- for( i = 0; i < numFiles; i++ ) {
+
+ qsort( list, num_files, sizeof( list[0] ), pakcmp );
+
+ for( i = 0; i < num_files; i++ ) {
len = Q_concat( path, sizeof( path ), fs_gamedir, "/", list[i], NULL );
if( len >= sizeof( path ) ) {
Com_EPrintf( "%s: refusing oversize path\n", __func__ );
@@ -1998,18 +1987,13 @@ static void FS_LoadPackFiles( int mode, const char *extension, pack_t *(loadfunc
FS_FreeList( list );
}
-/*
-================
-FS_AddGameDirectory
-
-Sets fs_gamedir, adds the directory to the head of the path,
-then loads and adds pak*.pak, then anything else in alphabethical order.
-================
-*/
-static void q_printf( 2, 3 ) FS_AddGameDirectory( int mode, const char *fmt, ... ) {
+// Sets fs_gamedir, adds the directory to the head of the path,
+// then loads and adds pak*.pak, then anything else in alphabethical order.
+static void q_printf( 2, 3 ) add_game_dir( unsigned mode, const char *fmt, ... ) {
va_list argptr;
searchpath_t *search;
size_t len;
+ //qerror_t ret;
va_start( argptr, fmt );
len = Q_vsnprintf( fs_gamedir, sizeof( fs_gamedir ), fmt, argptr );
@@ -2022,11 +2006,16 @@ static void q_printf( 2, 3 ) FS_AddGameDirectory( int mode, const char *fmt, ...
#ifdef _WIN32
FS_ReplaceSeparators( fs_gamedir, '/' );
+#elif 0
+ // check if this path exists and IS a directory
+ ret = Sys_GetPathInfo( fs_gamedir, NULL );
+ if( Q_ERRNO(ret) != EISDIR ) {
+ Com_DPrintf( "Not adding %s: %s\n", fs_gamedir, Q_ErrorString( ret ) );
+ return;
+ }
#endif
- //
// add the directory to the search path
- //
search = FS_Malloc( sizeof( searchpath_t ) + len );
search->mode = mode;
search->pack = NULL;
@@ -2034,16 +2023,12 @@ static void q_printf( 2, 3 ) FS_AddGameDirectory( int mode, const char *fmt, ...
search->next = fs_searchpaths;
fs_searchpaths = search;
- //
// add any pak files in the format *.pak
- //
- FS_LoadPackFiles( mode, ".pak", FS_LoadPakFile );
+ load_pack_files( mode, ".pak", load_pak_file );
#if USE_ZLIB
- //
// add any zip files in the format *.pkz
- //
- FS_LoadPackFiles( mode, ".pkz", FS_LoadZipFile );
+ load_pack_files( mode, ".pkz", load_zip_file );
#endif
}
@@ -2332,7 +2317,7 @@ void **FS_ListFiles( const char *path,
continue;
}
if( valid == -1 ) {
- if( !FS_ValidatePath( path ) ) {
+ if( validate_path( path ) ) {
FS_DPrintf( "%s: refusing invalid path: %s\n",
__func__, path );
valid = 0;
@@ -2532,7 +2517,10 @@ static void FS_WhereIs_f( void ) {
char *path;
file_info_t info;
int total;
+ int valid;
size_t len;
+ qerror_t ret;
+ qboolean report_all;
if( Cmd_Argc() < 2 ) {
Com_Printf( "Usage: %s <path> [all]\n", Cmd_Argv( 0 ) );
@@ -2540,15 +2528,17 @@ static void FS_WhereIs_f( void ) {
}
Cmd_ArgvBuffer( 1, filename, sizeof( filename ) );
+ report_all = Cmd_Argc() >= 3;
- path = FS_ExpandLinks( filename );
+ path = expand_links( filename );
if( path != filename ) {
- Com_Printf( "%s linked to %s\n", filename, path );
+ Com_Printf( "%s is linked to %s\n", filename, path );
}
hash = Com_HashPath( path, 0 );
total = 0;
+ valid = -1;
for( search = fs_searchpaths; search; search = search->next ) {
// is the element a pak file?
if( search->pack ) {
@@ -2559,25 +2549,44 @@ static void FS_WhereIs_f( void ) {
if( !FS_pathcmp( entry->name, path ) ) {
Com_Printf( "%s/%s (%"PRIz" bytes)\n", pak->filename,
path, entry->filelen );
- if( Cmd_Argc() < 3 ) {
+ if( !report_all ) {
return;
}
total++;
}
}
} else {
+ if( valid == -1 ) {
+ ret = validate_path( path );
+ if( ret ) {
+ Com_WPrintf( "Not searching for %s in real file system: %s\n",
+ path, Q_ErrorString( ret ) );
+ valid = 0;
+ }
+ }
+ if( valid == 0 ) {
+ continue;
+ }
len = Q_concat( fullpath, sizeof( fullpath ),
search->filename, "/", path, NULL );
if( len >= sizeof( fullpath ) ) {
- continue;
+ ret = Q_ERR_NAMETOOLONG;
+ goto fail;
}
//FS_ConvertToSysPath( fullpath );
- if( Sys_GetPathInfo( fullpath, &info ) ) {
- Com_Printf( "%s/%s (%"PRIz" bytes)\n", search->filename, filename, info.size );
- if( Cmd_Argc() < 3 ) {
+ ret = Sys_GetPathInfo( fullpath, &info );
+ if( !ret ) {
+ Com_Printf( "%s (%"PRIz" bytes)\n", fullpath, info.size );
+ if( !report_all ) {
return;
}
total++;
+ } else if( ret != Q_ERR_NOENT ) {
+fail:
+ Com_EPrintf( "Couldn't get info on %s: %s\n", fullpath, Q_ErrorString( ret ) );
+ if( !report_all ) {
+ return;
+ }
}
}
@@ -2666,10 +2675,6 @@ static void FS_Stats_f( void ) {
}
#ifdef _DEBUG
-#if USE_LOADBUF
- Com_Printf( "LoadFile counter: %d\n", loadCount );
- Com_Printf( "Static LoadFile counter: %d\n", loadCountStatic );
-#endif
Com_Printf( "Total calls to OpenFileRead: %d\n", fs_count_read );
Com_Printf( "Total path comparsions: %d\n", fs_count_strcmp );
Com_Printf( "Total calls to fopen: %d\n", fs_count_open );
@@ -2837,28 +2842,28 @@ static void free_game_paths( void ) {
fs_searchpaths = fs_base_searchpaths;
}
-static void setup_basedir( void ) {
- FS_AddGameDirectory( FS_PATH_BASE|FS_PATH_GAME, "%s/"BASEGAME, sys_basedir->string );
+static void setup_base_paths( void ) {
+ add_game_dir( FS_PATH_BASE|FS_PATH_GAME, "%s/"BASEGAME, sys_basedir->string );
fs_base_searchpaths = fs_searchpaths;
}
// Sets the gamedir and path to a different directory.
-static void setup_gamedir( void ) {
+static void setup_game_paths( void ) {
if( fs_game->string[0] ) {
// add system path first
- FS_AddGameDirectory( FS_PATH_GAME, "%s/%s", sys_basedir->string, fs_game->string );
+ add_game_dir( FS_PATH_GAME, "%s/%s", sys_basedir->string, fs_game->string );
// home paths override system paths
if( sys_homedir->string[0] ) {
- FS_AddGameDirectory( FS_PATH_BASE, "%s/"BASEGAME, sys_homedir->string );
- FS_AddGameDirectory( FS_PATH_GAME, "%s/%s", sys_homedir->string, fs_game->string );
+ add_game_dir( FS_PATH_BASE, "%s/"BASEGAME, sys_homedir->string );
+ add_game_dir( FS_PATH_GAME, "%s/%s", sys_homedir->string, fs_game->string );
}
// this var is set for compatibility with server browsers, etc
Cvar_FullSet( "gamedir", fs_game->string, CVAR_ROM|CVAR_SERVERINFO, FROM_CODE );
} else {
if( sys_homedir->string[0] ) {
- FS_AddGameDirectory( FS_PATH_BASE|FS_PATH_GAME,
+ add_game_dir( FS_PATH_BASE|FS_PATH_GAME,
"%s/"BASEGAME, sys_homedir->string );
}
@@ -2882,13 +2887,13 @@ void FS_Restart( qboolean total ) {
if( total ) {
// perform full reset
free_all_paths();
- setup_basedir();
+ setup_base_paths();
} else {
// just change gamedir
free_game_paths();
}
- setup_gamedir();
+ setup_game_paths();
FS_Path_f();
@@ -2978,10 +2983,10 @@ static void fs_game_changed( cvar_t *self ) {
// check for the first time startup
if( !fs_base_searchpaths ) {
// start up with baseq2 by default
- setup_basedir();
+ setup_base_paths();
// check for game override
- setup_gamedir();
+ setup_game_paths();
FS_Path_f();
return;
diff --git a/source/files.h b/source/files.h
index e352bec..135757e 100644
--- a/source/files.h
+++ b/source/files.h
@@ -28,18 +28,18 @@ typedef struct file_info_s {
} file_info_t;
/* bits 0 - 1, enum */
-#define FS_MODE_APPEND 0x00000000
-#define FS_MODE_READ 0x00000001
-#define FS_MODE_WRITE 0x00000002
-#define FS_MODE_RDWR 0x00000003
-#define FS_MODE_MASK 0x00000003
+#define FS_MODE_APPEND 0x00000000
+#define FS_MODE_READ 0x00000001
+#define FS_MODE_WRITE 0x00000002
+#define FS_MODE_RDWR 0x00000003
+#define FS_MODE_MASK 0x00000003
/* bits 0 - 1, enum */
-#define FS_SEARCHDIRS_NO 0x00000000
-#define FS_SEARCHDIRS_YES 0x00000001
-#define FS_SEARCHDIRS_ONLY 0x00000002
-#define FS_SEARCHDIRS_RESERVED 0x00000003
-#define FS_SEARCHDIRS_MASK 0x00000003
+#define FS_SEARCHDIRS_NO 0x00000000
+#define FS_SEARCHDIRS_YES 0x00000001
+#define FS_SEARCHDIRS_ONLY 0x00000002
+#define FS_SEARCHDIRS_RESERVED 0x00000003
+#define FS_SEARCHDIRS_MASK 0x00000003
/* bit 2, enum */
#define FS_FLUSH_NONE 0x00000000
@@ -47,17 +47,17 @@ typedef struct file_info_s {
#define FS_FLUSH_MASK 0x00000004
/* bits 3 - 4, enum */
-#define FS_TYPE_ANY 0x00000000
-#define FS_TYPE_REAL 0x00000008
-#define FS_TYPE_PAK 0x00000010
-#define FS_TYPE_RESERVED 0x00000018
-#define FS_TYPE_MASK 0x00000018
+#define FS_TYPE_ANY 0x00000000
+#define FS_TYPE_REAL 0x00000008
+#define FS_TYPE_PAK 0x00000010
+#define FS_TYPE_RESERVED 0x00000018
+#define FS_TYPE_MASK 0x00000018
/* bits 5 - 6, flag */
-#define FS_PATH_ANY 0x00000000
-#define FS_PATH_BASE 0x00000020
-#define FS_PATH_GAME 0x00000040
-#define FS_PATH_MASK 0x00000060
+#define FS_PATH_ANY 0x00000000
+#define FS_PATH_BASE 0x00000020
+#define FS_PATH_GAME 0x00000040
+#define FS_PATH_MASK 0x00000060
/* bits 7 - 10, flag */
#define FS_SEARCH_BYFILTER 0x00000080
@@ -66,51 +66,63 @@ typedef struct file_info_s {
#define FS_SEARCH_NOSORT 0x00000400
/* bits 7 - 8, flag */
-#define FS_FLAG_RESERVED1 0x00000080
-#define FS_FLAG_RESERVED2 0x00000100
-
-#define INVALID_LENGTH ((size_t)-1)
+#define FS_FLAG_GZIP 0x00000080
+#define FS_FLAG_EXCL 0x00000100
#define FS_Malloc( size ) Z_TagMalloc( size, TAG_FILESYSTEM )
#define FS_Mallocz( size ) Z_TagMallocz( size, TAG_FILESYSTEM )
-#define FS_CopyString( string ) Z_TagCopyString( string, TAG_FILESYSTEM )
+#define FS_CopyString( string ) Z_TagCopyString( string, TAG_FILESYSTEM )
+
+// protection from malicious paks causing memory exhaustion
+// no loadable Q2 resource should ever exceed this limit
+#define MAX_LOADFILE 0x400000 // 64 MiB
void FS_Init( void );
void FS_Shutdown( void );
-void FS_Restart( qboolean total );
+void FS_Restart( qboolean total );
#if USE_CLIENT
-qboolean FS_RenameFile( const char *from, const char *to );
+qerror_t FS_RenameFile( const char *from, const char *to );
#endif
-void FS_CreatePath( char *path );
+qerror_t FS_CreatePath( char *path );
char *FS_CopyExtraInfo( const char *name, const file_info_t *info );
-size_t FS_FOpenFile( const char *filename, fileHandle_t *f, int mode );
-void FS_FCloseFile( fileHandle_t hFile );
-qboolean FS_FilterFile( fileHandle_t f );
+ssize_t FS_FOpenFile( const char *filename, qhandle_t *f, unsigned mode );
+void FS_FCloseFile( qhandle_t f );
+qhandle_t FS_EasyOpenFile( char *buf, size_t size, unsigned mode,
+ const char *dir, const char *name, const char *ext );
+
+qerror_t FS_FilterFile( qhandle_t f );
-size_t FS_LoadFile( const char *path, void **buffer );
-size_t FS_LoadFileEx( const char *path, void **buffer, int flags, memtag_t tag );
-void *FS_AllocTempMem( size_t length );
-void FS_FreeFile( void *buffer );
+#define FS_FileExistsEx( path, flags ) \
+ ( FS_LoadFileEx( path, NULL, flags ) != Q_ERR_NOENT )
+#define FS_FileExists( path ) \
+ FS_FileExistsEx( path, 0 )
+
+ssize_t FS_LoadFile( const char *path, void **buffer );
+ssize_t FS_LoadFileEx( const char *path, void **buffer, unsigned flags );
+void *FS_AllocTempMem( size_t len );
+void FS_FreeFile( void *buf );
// a null buffer will just return the file length without loading
// a -1 length is not present
-size_t FS_Read( void *buffer, size_t len, fileHandle_t hFile );
-size_t FS_Write( const void *buffer, size_t len, fileHandle_t hFile );
+qerror_t FS_WriteFile( const char *path, const void *data, size_t len );
+
+ssize_t FS_Read( void *buffer, size_t len, qhandle_t f );
+ssize_t FS_Write( const void *buffer, size_t len, qhandle_t f );
// properly handles partial reads
-void FS_FPrintf( fileHandle_t f, const char *format, ... ) q_printf( 2, 3 );
-size_t FS_ReadLine( fileHandle_t f, char *buffer, int size );
+ssize_t FS_FPrintf( qhandle_t f, const char *format, ... ) q_printf( 2, 3 );
+ssize_t FS_ReadLine( qhandle_t f, char *buffer, size_t size );
-void FS_Flush( fileHandle_t f );
+void FS_Flush( qhandle_t f );
-size_t FS_Tell( fileHandle_t f );
-qboolean FS_Seek( fileHandle_t f, size_t offset );
+ssize_t FS_Tell( qhandle_t f );
+qerror_t FS_Seek( qhandle_t f, size_t offset );
-size_t FS_GetFileLength( fileHandle_t f );
+ssize_t FS_GetFileLength( qhandle_t f );
qboolean FS_WildCmp( const char *filter, const char *string );
qboolean FS_ExtCmp( const char *extension, const char *string );
@@ -120,8 +132,6 @@ void **FS_CopyList( void **list, int count );
file_info_t *FS_CopyInfo( const char *name, size_t size, time_t ctime, time_t mtime );
void FS_FreeList( void **list );
-qboolean FS_LastFileFromPak( void );
-
char *FS_ReplaceSeparators( char *s, int separator );
int FS_pathcmp( const char *s1, const char *s2 );
diff --git a/source/gl_images.c b/source/gl_images.c
index 369e5ae..09cf01d 100644
--- a/source/gl_images.c
+++ b/source/gl_images.c
@@ -762,7 +762,7 @@ static void GL_GetDimensions( image_t *image, imageflags_t flags ) {
size_t length;
miptex_t mt;
dpcx_t pcx;
- fileHandle_t f;
+ qhandle_t f;
length = strlen( image->name );
if( length > 4 && image->name[ length - 4 ] == '.' ) {
@@ -902,12 +902,18 @@ IMG_LoadWAL
*/
image_t *IMG_LoadWAL( const char *name ) {
miptex_t *mt;
- size_t width, height, offset, length, endpos;
+ size_t width, height, offset, len, endpos;
image_t *image;
+ qerror_t ret;
- length = FS_LoadFile( name, ( void ** )&mt );
+ len = FS_LoadFile( name, ( void ** )&mt );
if( !mt ) {
- return NULL;
+ // don't spam about missing images
+ if( len == Q_ERR_NOENT ) {
+ return NULL;
+ }
+ ret = len;
+ goto fail1;
}
width = LittleLong( mt->width );
@@ -915,13 +921,14 @@ image_t *IMG_LoadWAL( const char *name ) {
offset = LittleLong( mt->offsets[0] );
if( width < 1 || height < 1 || width > MAX_TEXTURE_SIZE || height > MAX_TEXTURE_SIZE ) {
- Com_WPrintf( "LoadWAL: %s: bad dimensions\n", name );
- goto fail;
+ ret = Q_ERR_INVALID_FORMAT;
+ goto fail2;
}
+
endpos = offset + width * height;
- if( endpos < offset || endpos > length ) {
- Com_WPrintf( "LoadWAL: %s: bad offset\n", name );
- goto fail;
+ if( endpos < offset || endpos > len ) {
+ ret = Q_ERR_BAD_EXTENT;
+ goto fail2;
}
image = IMG_Create( name, ( byte * )mt + offset, width, height, it_wall, if_paletted );
@@ -929,8 +936,10 @@ image_t *IMG_LoadWAL( const char *name ) {
FS_FreeFile( mt );
return image;
-fail:
+fail2:
FS_FreeFile( mt );
+fail1:
+ Com_EPrintf( "Couldn't load %s: %s\n", name, Q_ErrorString( ret ) );
return NULL;
}
diff --git a/source/gl_main.c b/source/gl_main.c
index 2a8c2f6..c4cb998 100644
--- a/source/gl_main.c
+++ b/source/gl_main.c
@@ -534,34 +534,59 @@ void R_EndFrame( void ) {
*/
#if USE_TGA || USE_JPG || USE_PNG
-static char *screenshot_path( char *buffer, const char *ext ) {
- int i;
- size_t len;
+static void make_screenshot( const char *ext, img_save_t func, GLenum format, int param ) {
+ char buffer[MAX_OSPATH];
+ byte *pixels;
+ qerror_t ret;
+ qhandle_t f;
+ int i;
if( Cmd_Argc() > 1 ) {
- len = Q_concat( buffer, MAX_OSPATH, SCREENSHOTS_DIRECTORY "/", Cmd_Argv( 1 ), ext, NULL );
- if( len >= MAX_OSPATH ) {
- Com_EPrintf( "Oversize filename specified.\n" );
- return NULL;
+ f = FS_EasyOpenFile( buffer, sizeof( buffer ), FS_MODE_WRITE,
+ SCREENSHOTS_DIRECTORY "/", Cmd_Argv( 1 ), ext );
+ if( !f ) {
+ return;
}
- return buffer;
- }
-//
-// find a file name to save it to
-//
- for( i = 0; i < 1000; i++ ) {
- Q_snprintf( buffer, MAX_OSPATH, SCREENSHOTS_DIRECTORY"/quake%03d%s", i, ext );
- if( FS_LoadFileEx( buffer, NULL, FS_PATH_GAME, TAG_FREE ) == INVALID_LENGTH ) {
- return buffer; // file doesn't exist
+ } else {
+ // find a file name to save it to
+ for( i = 0; i < 1000; i++ ) {
+ Q_snprintf( buffer, sizeof( buffer ), SCREENSHOTS_DIRECTORY "/quake%03d%s", i, ext );
+ ret = FS_FOpenFile( buffer, &f, FS_MODE_WRITE|FS_FLAG_EXCL );
+ if( f ) {
+ break;
+ }
+ if( ret != Q_ERR_EXIST ) {
+ Com_EPrintf( "Couldn't exclusively open %s for writing: %s\n",
+ buffer, Q_ErrorString( ret ) );
+ return;
+ }
+ }
+
+ if( i == 1000 ) {
+ Com_EPrintf( "All screenshot slots are full.\n" );
+ return;
}
}
- Com_Printf( "All screenshot slots are full.\n" );
- return NULL;
+ pixels = FS_AllocTempMem( gl_config.vidWidth * gl_config.vidHeight * 3 );
+
+ qglReadPixels( 0, 0, gl_config.vidWidth, gl_config.vidHeight, format,
+ GL_UNSIGNED_BYTE, pixels );
+
+ ret = func( f, buffer, pixels, gl_config.vidWidth, gl_config.vidHeight, param );
+
+ FS_FreeFile( pixels );
+
+ FS_FCloseFile( f );
+
+ if( ret < 0 ) {
+ Com_EPrintf( "Couldn't write %s: %s\n", buffer, Q_ErrorString( ret ) );
+ } else {
+ Com_Printf( "Wrote %s\n", buffer );
+ }
}
#endif
-
/*
==================
GL_ScreenShot_f
@@ -569,31 +594,11 @@ GL_ScreenShot_f
*/
static void GL_ScreenShot_f( void ) {
#if USE_TGA
- char buffer[MAX_OSPATH];
- byte *bgr;
- qboolean ret;
-
if( Cmd_Argc() > 2 ) {
Com_Printf( "Usage: %s [name]\n", Cmd_Argv( 0 ) );
return;
}
-
- if( !screenshot_path( buffer, ".tga" ) ) {
- return;
- }
-
- bgr = FS_AllocTempMem( gl_config.vidWidth * gl_config.vidHeight * 3 );
-
- qglReadPixels( 0, 0, gl_config.vidWidth, gl_config.vidHeight, GL_BGR,
- GL_UNSIGNED_BYTE, bgr );
-
- ret = IMG_WriteTGA( buffer, bgr, gl_config.vidWidth, gl_config.vidHeight );
-
- FS_FreeFile( bgr );
-
- if( ret ) {
- Com_Printf( "Wrote %s\n", buffer );
- }
+ make_screenshot( ".tga", IMG_SaveTGA, GL_BGR, 0 );
#else
Com_Printf( "Couldn't create screenshot due to no TGA support linked in.\n" );
#endif
@@ -601,75 +606,39 @@ static void GL_ScreenShot_f( void ) {
#if USE_JPG
static void GL_ScreenShotJPG_f( void ) {
- char buffer[MAX_OSPATH];
- byte *rgb;
- int quality;
- qboolean ret;
+ int quality;
if( Cmd_Argc() > 3 ) {
Com_Printf( "Usage: %s [name] [quality]\n", Cmd_Argv( 0 ) );
return;
}
- if( !screenshot_path( buffer, ".jpg" ) ) {
- return;
- }
-
- rgb = FS_AllocTempMem( gl_config.vidWidth * gl_config.vidHeight * 3 );
-
- qglReadPixels( 0, 0, gl_config.vidWidth, gl_config.vidHeight, GL_RGB,
- GL_UNSIGNED_BYTE, rgb );
-
if( Cmd_Argc() > 2 ) {
quality = atoi( Cmd_Argv( 2 ) );
} else {
quality = gl_screenshot_quality->integer;
}
- ret = IMG_WriteJPG( buffer, rgb, gl_config.vidWidth, gl_config.vidHeight, quality );
-
- FS_FreeFile( rgb );
-
- if( ret ) {
- Com_Printf( "Wrote %s\n", buffer );
- }
+ make_screenshot( ".jpg", IMG_SaveJPG, GL_RGB, quality );
}
#endif
#if USE_PNG
static void GL_ScreenShotPNG_f( void ) {
- char buffer[MAX_OSPATH];
- byte *rgb;
- int compression;
- qboolean ret;
+ int compression;
if( Cmd_Argc() > 3 ) {
Com_Printf( "Usage: %s [name] [compression]\n", Cmd_Argv( 0 ) );
return;
}
- if( !screenshot_path( buffer, ".png" ) ) {
- return;
- }
-
- rgb = FS_AllocTempMem( gl_config.vidWidth * gl_config.vidHeight * 3 );
-
- qglReadPixels( 0, 0, gl_config.vidWidth, gl_config.vidHeight, GL_RGB,
- GL_UNSIGNED_BYTE, rgb );
-
if( Cmd_Argc() > 2 ) {
compression = atoi( Cmd_Argv( 2 ) );
} else {
compression = gl_screenshot_compression->integer;
}
- ret = IMG_WritePNG( buffer, rgb, gl_config.vidWidth, gl_config.vidHeight, compression );
-
- FS_FreeFile( rgb );
-
- if( ret ) {
- Com_Printf( "Wrote %s\n", buffer );
- }
+ make_screenshot( ".png", IMG_SavePNG, GL_RGB, compression );
}
#endif
diff --git a/source/gl_models.c b/source/gl_models.c
index 8d1f2b7..264495e 100644
--- a/source/gl_models.c
+++ b/source/gl_models.c
@@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "d_md3.h"
#include "d_sp2.h"
-qboolean MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) {
+qerror_t MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) {
dmd2header_t header;
dmd2frame_t *src_frame;
dmd2trivertx_t *src_vert;
@@ -45,25 +45,27 @@ qboolean MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) {
vec_t scaleS, scaleT;
int val;
vec3_t mins, maxs;
+ qerror_t ret;
if( length < sizeof( header ) ) {
- Com_WPrintf( "%s is too short\n", model->name );
- return qfalse;
+ return Q_ERR_FILE_TOO_SMALL;
}
- /* byte swap the header */
+ // byte swap the header
header = *( dmd2header_t * )rawdata;
for( i = 0; i < sizeof( header )/4; i++ ) {
(( uint32_t * )&header)[i] = LittleLong( (( uint32_t * )&header)[i] );
}
- if( !MOD_ValidateMD2( model, &header, length ) ) {
- return qfalse;
+ // validate the header
+ ret = MOD_ValidateMD2( model, &header, length );
+ if( ret ) {
+ return ret;
}
Hunk_Begin( &model->pool, 0x400000 );
- /* load all triangle indices */
+ // load all triangle indices
src_tri = ( dmd2triangle_t * )( ( byte * )rawdata + header.ofs_tris );
for( i = 0; i < header.num_tris; i++ ) {
for( j = 0; j < 3; j++ ) {
@@ -71,7 +73,7 @@ qboolean MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) {
uint16_t idx_st = LittleShort( src_tri->index_st[j] );
if( idx_xyz >= header.num_xyz || idx_st >= header.num_st ) {
- Com_WPrintf( "%s has bad triangle indices\n", model->name );
+ ret = Q_ERR_BAD_INDEX;
goto fail;
}
@@ -100,7 +102,7 @@ qboolean MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) {
finalIndices = dst_mesh->indices;
for( i = 0; i < numindices; i++ ) {
if( remap[i] != 0xFFFF ) {
- continue; /* already remapped */
+ continue; // already remapped
}
for( j = i + 1; j < numindices; j++ ) {
@@ -108,13 +110,13 @@ qboolean MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) {
( src_tc[tcIndices[i]].s == src_tc[tcIndices[j]].s &&
src_tc[tcIndices[i]].t == src_tc[tcIndices[j]].t ) )
{
- /* duplicate vertex */
+ // duplicate vertex
remap[j] = i;
finalIndices[j] = numverts;
}
}
- /* new vertex */
+ // new vertex
remap[i] = i;
finalIndices[i] = numverts++;
}
@@ -123,7 +125,7 @@ qboolean MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) {
dst_mesh->tcoords = Model_Malloc( numverts * sizeof( maliastc_t ) );
dst_mesh->numverts = numverts;
- /* load all skins */
+ // load all skins
src_skin = ( char * )rawdata + header.ofs_skins;
for( i = 0; i < header.num_skins; i++ ) {
Q_strlcpy( skinname, src_skin, sizeof( skinname ) );
@@ -136,7 +138,7 @@ qboolean MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) {
}
dst_mesh->numskins = header.num_skins;
- /* load all tcoords */
+ // load all tcoords
src_tc = ( dmd2stvert_t * )( ( byte * )rawdata + header.ofs_st );
dst_tc = dst_mesh->tcoords;
skin = dst_mesh->skins[0];
@@ -152,7 +154,7 @@ qboolean MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) {
}
}
- /* load all frames */
+ // load all frames
model->frames = Model_Malloc( header.num_frames * sizeof( maliasframe_t ) );
model->numframes = header.num_frames;
@@ -162,7 +164,7 @@ qboolean MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) {
LittleVector( src_frame->scale, dst_frame->scale );
LittleVector( src_frame->translate, dst_frame->translate );
- /* load frame vertices */
+ // load frame vertices
ClearBounds( mins, maxs );
for( i = 0; i < numindices; i++ ) {
if( remap[i] == i ) {
@@ -174,7 +176,7 @@ qboolean MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) {
dst_vert->pos[2] = src_vert->v[2];
k = src_vert->lightnormalindex;
if( k >= NUMVERTEXNORMALS ) {
- Com_WPrintf( "%s has bad normal indices\n", model->name );
+ ret = Q_ERR_BAD_INDEX;
goto fail;
}
dst_vert->normalindex = k;
@@ -202,11 +204,11 @@ qboolean MOD_LoadMD2( model_t *model, const void *rawdata, size_t length ) {
}
Hunk_End( &model->pool );
- return qtrue;
+ return Q_ERR_SUCCESS;
fail:
Hunk_Free( &model->pool );
- return qfalse;
+ return ret;
}
#if USE_MD3
@@ -232,7 +234,7 @@ static void ll2byte_init( void ) {
}
}
-qboolean MOD_LoadMD3( model_t *model, const void *rawdata, size_t length ) {
+qerror_t MOD_LoadMD3( model_t *model, const void *rawdata, size_t length ) {
dmd3header_t header;
uint32_t offset;
dmd3frame_t *src_frame;
@@ -252,35 +254,30 @@ qboolean MOD_LoadMD3( model_t *model, const void *rawdata, size_t length ) {
image_t *skin;
const byte *rawend;
int i, j;
+ qerror_t ret;
if( length < sizeof( header ) ) {
- Com_WPrintf( "%s is too small to hold MD3 header\n", model->name );
- return qfalse;
+ return Q_ERR_FILE_TOO_SMALL;
}
- /* byte swap the header */
+ // byte swap the header
header = *( dmd3header_t * )rawdata;
for( i = 0; i < sizeof( header )/4; i++ ) {
(( uint32_t * )&header)[i] = LittleLong( (( uint32_t * )&header)[i] );
}
- if( header.ident != MD3_IDENT ) {
- Com_WPrintf( "%s is not an MD3 file\n", model->name );
- return qfalse;
- }
- if( header.version != MD3_VERSION ) {
- Com_WPrintf( "%s has bad version: %d instead of %d\n",
- model->name, header.version, MD3_VERSION );
- return qfalse;
- }
- if( header.num_frames < 1 || header.num_frames > MD3_MAX_FRAMES ) {
- Com_WPrintf( "%s has bad number of frames\n", model->name );
- return qfalse;
- }
- if( header.num_meshes < 1 || header.num_meshes > MD3_MAX_MESHES ) {
- Com_WPrintf( "%s has bad number of meshes\n", model->name );
- return qfalse;
- }
+ if( header.ident != MD3_IDENT )
+ return Q_ERR_UNKNOWN_FORMAT;
+ if( header.version != MD3_VERSION )
+ return Q_ERR_UNKNOWN_FORMAT;
+ if( header.num_frames < 1 )
+ return Q_ERR_TOO_FEW;
+ if( header.num_frames > MD3_MAX_FRAMES )
+ return Q_ERR_TOO_MANY;
+ if( header.num_meshes < 1 )
+ return Q_ERR_TOO_FEW;
+ if( header.num_meshes > MD3_MAX_MESHES )
+ return Q_ERR_TOO_MANY;
Hunk_Begin( &model->pool, 0x400000 );
model->numframes = header.num_frames;
@@ -290,10 +287,10 @@ qboolean MOD_LoadMD3( model_t *model, const void *rawdata, size_t length ) {
rawend = ( byte * )rawdata + length;
- /* load all frames */
+ // load all frames
src_frame = ( dmd3frame_t * )( ( byte * )rawdata + header.ofs_frames );
if( ( byte * )( src_frame + header.num_frames ) > rawend ) {
- Com_WPrintf( "%s has bad frames offset\n", model->name );
+ ret = Q_ERR_BAD_EXTENT;
goto fail;
}
dst_frame = model->frames;
@@ -307,25 +304,38 @@ qboolean MOD_LoadMD3( model_t *model, const void *rawdata, size_t length ) {
src_frame++; dst_frame++;
}
+
+ if( !ll2byte_inited ) {
+ ll2byte_init();
+ ll2byte_inited = qtrue;
+ }
- /* load all meshes */
+ // load all meshes
src_mesh = ( dmd3mesh_t * )( ( byte * )rawdata + header.ofs_meshes );
dst_mesh = model->meshes;
for( i = 0; i < header.num_meshes; i++ ) {
if( ( byte * )( src_mesh + 1 ) > rawend ) {
- Com_WPrintf( "%s has bad offset for mesh %d\n", model->name, i );
+ ret = Q_ERR_BAD_EXTENT;
goto fail;
}
numverts = LittleLong( src_mesh->num_verts );
- numtris = LittleLong( src_mesh->num_tris );
+ if( numverts < 3 ) {
+ ret = Q_ERR_TOO_FEW;
+ goto fail;
+ }
+ if( numverts > TESS_MAX_VERTICES ) {
+ ret = Q_ERR_TOO_MANY;
+ goto fail;
+ }
- if( numverts < 3 || numverts > TESS_MAX_VERTICES ) {
- Com_WPrintf( "%s has bad number of vertices for mesh %d\n", model->name, i );
+ numtris = LittleLong( src_mesh->num_tris );
+ if( numtris < 1 ) {
+ ret = Q_ERR_TOO_FEW;
goto fail;
}
- if( numtris < 1 || numtris > TESS_MAX_INDICES / 3 ) {
- Com_WPrintf( "%s has bad number of faces for mesh %d\n", model->name, i );
+ if( numtris > TESS_MAX_INDICES / 3 ) {
+ ret = Q_ERR_TOO_MANY;
goto fail;
}
@@ -336,16 +346,16 @@ qboolean MOD_LoadMD3( model_t *model, const void *rawdata, size_t length ) {
dst_mesh->tcoords = Model_Malloc( sizeof( maliastc_t ) * numverts );
dst_mesh->indices = Model_Malloc( sizeof( uint32_t ) * numtris * 3 );
- /* load all skins */
+ // load all skins
numskins = LittleLong( src_mesh->num_skins );
if( numskins > MAX_ALIAS_SKINS ) {
- Com_WPrintf( "%s has bad number of skins for mesh %d\n", model->name, i );
+ ret = Q_ERR_TOO_MANY;
goto fail;
}
offset = LittleLong( src_mesh->ofs_skins );
src_skin = ( dmd3skin_t * )( ( byte * )src_mesh + offset );
if( ( byte * )( src_skin + numskins ) > rawend ) {
- Com_WPrintf( "%s has bad skins offset for mesh %d\n", model->name, i );
+ ret = Q_ERR_BAD_EXTENT;
goto fail;
}
for( j = 0; j < numskins; j++ ) {
@@ -358,12 +368,12 @@ qboolean MOD_LoadMD3( model_t *model, const void *rawdata, size_t length ) {
}
dst_mesh->numskins = numskins;
- /* load all vertices */
+ // load all vertices
totalVerts = numverts * header.num_frames;
offset = LittleLong( src_mesh->ofs_verts );
src_vert = ( dmd3vertex_t * )( ( byte * )src_mesh + offset );
if( ( byte * )( src_vert + totalVerts ) > rawend ) {
- Com_WPrintf( "%s has bad vertices offset for mesh %d\n", model->name, i );
+ ret = Q_ERR_BAD_EXTENT;
goto fail;
}
dst_vert = dst_mesh->verts;
@@ -372,21 +382,16 @@ qboolean MOD_LoadMD3( model_t *model, const void *rawdata, size_t length ) {
dst_vert->pos[1] = ( signed short )LittleShort( src_vert->point[1] );
dst_vert->pos[2] = ( signed short )LittleShort( src_vert->point[2] );
- if( !ll2byte_inited ) {
- ll2byte_init();
- ll2byte_inited = qtrue;
- }
- dst_vert->normalindex = ll2byte[src_vert->norm[0]]
- [src_vert->norm[1]];
+ dst_vert->normalindex = ll2byte[src_vert->norm[0]][src_vert->norm[1]];
src_vert++; dst_vert++;
}
- /* load all texture coords */
+ // load all texture coords
offset = LittleLong( src_mesh->ofs_tcs );
src_tc = ( dmd3coord_t * )( ( byte * )src_mesh + offset );
if( ( byte * )( src_tc + numverts ) > rawend ) {
- Com_WPrintf( "%s has bad tcoords offset for mesh %d\n", model->name, i );
+ ret = Q_ERR_BAD_EXTENT;
goto fail;
}
dst_tc = dst_mesh->tcoords;
@@ -396,11 +401,11 @@ qboolean MOD_LoadMD3( model_t *model, const void *rawdata, size_t length ) {
src_tc++; dst_tc++;
}
- /* load all triangle indices */
+ // load all triangle indices
offset = LittleLong( src_mesh->ofs_indexes );
src_idx = ( uint32_t * )( ( byte * )src_mesh + offset );
if( ( byte * )( src_idx + numtris * 3 ) > rawend ) {
- Com_WPrintf( "%s has bad indices offset for mesh %d\n", model->name, i );
+ ret = Q_ERR_BAD_EXTENT;
goto fail;
}
dst_idx = dst_mesh->indices;
@@ -409,7 +414,7 @@ qboolean MOD_LoadMD3( model_t *model, const void *rawdata, size_t length ) {
dst_idx[1] = LittleLong( src_idx[1] );
dst_idx[2] = LittleLong( src_idx[2] );
if( dst_idx[0] >= numverts || dst_idx[1] >= numverts || dst_idx[2] >= numverts ) {
- Com_WPrintf( "%s has bad indices for triangle %d in mesh %d\n", model->name, j, i );
+ ret = Q_ERR_BAD_INDEX;
goto fail;
}
src_idx += 3; dst_idx += 3;
@@ -421,11 +426,11 @@ qboolean MOD_LoadMD3( model_t *model, const void *rawdata, size_t length ) {
}
Hunk_End( &model->pool );
- return qtrue;
+ return Q_ERR_SUCCESS;
fail:
Hunk_Free( &model->pool );
- return qfalse;
+ return ret;
}
#endif
diff --git a/source/gl_sky.c b/source/gl_sky.c
index fd0a305..163ae7f 100644
--- a/source/gl_sky.c
+++ b/source/gl_sky.c
@@ -387,6 +387,7 @@ void R_SetSky( const char *name, float rotate, vec3_t axis ) {
int i;
char pathname[MAX_QPATH];
image_t *image;
+ size_t len;
// 3dstudio environment map names
static const char suf[6][3] = { "rt", "bk", "lf", "ft", "up", "dn" };
@@ -399,8 +400,12 @@ void R_SetSky( const char *name, float rotate, vec3_t axis ) {
VectorCopy (axis, skyaxis);
for( i = 0; i < 6; i++ ) {
- Q_concat( pathname, sizeof( pathname ),
+ len = Q_concat( pathname, sizeof( pathname ),
"env/", name, suf[i], ".tga", NULL );
+ if( len >= sizeof( pathname ) ) {
+ R_UnsetSky();
+ return;
+ }
image = IMG_Find( pathname, it_sky );
if( !image ) {
R_UnsetSky();
diff --git a/source/gl_surf.c b/source/gl_surf.c
index c514c0a..b696bf2 100644
--- a/source/gl_surf.c
+++ b/source/gl_surf.c
@@ -300,10 +300,12 @@ void GL_LoadWorld( const char *name ) {
bsp_t *bsp;
mtexinfo_t *info;
image_t *image;
+ qerror_t ret;
- if( !( bsp = BSP_Load( name ) ) ) {
+ ret = BSP_Load( name, &bsp );
+ if( !bsp ) {
Com_Error( ERR_DROP, "%s: couldn't load %s: %s",
- __func__, name, BSP_GetError() );
+ __func__, name, Q_ErrorString( ret ) );
}
// check if the required world model was already loaded
diff --git a/source/in_evdev.c b/source/in_evdev.c
index 1085398..3550c09 100644
--- a/source/in_evdev.c
+++ b/source/in_evdev.c
@@ -30,7 +30,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <unistd.h>
#include <fcntl.h>
#include <linux/input.h>
-#include <errno.h>
#include <SDL.h>
@@ -47,12 +46,7 @@ static struct {
#define MAX_EVENTS 64
#define EVENT_SIZE sizeof( struct input_event )
-/*
-===========
-Evdev_GetMouseEvents
-===========
-*/
-static void Evdev_GetMouseEvents( void ) {
+static void GetMouseEvents( void ) {
struct input_event ev[MAX_EVENTS];
int bytes, count;
int dx, dy;
@@ -109,7 +103,7 @@ static void Evdev_GetMouseEvents( void ) {
}
}
-static qboolean Evdev_GetMouseMotion( int *dx, int *dy ) {
+static qboolean GetMouseMotion( int *dx, int *dy ) {
if( !evdev.initialized || !evdev.grabbed ) {
return qfalse;
}
@@ -120,12 +114,7 @@ static qboolean Evdev_GetMouseMotion( int *dx, int *dy ) {
return qtrue;
}
-/*
-===========
-Evdev_ShutdownMouse
-===========
-*/
-static void Evdev_ShutdownMouse( void ) {
+static void ShutdownMouse( void ) {
if( !evdev.initialized ) {
return;
}
@@ -134,15 +123,10 @@ static void Evdev_ShutdownMouse( void ) {
memset( &evdev, 0, sizeof( evdev ) );
}
-/*
-===========
-Evdev_StartupMouse
-===========
-*/
-static qboolean Evdev_InitMouse( void ) {
+static qboolean InitMouse( void ) {
in_device = Cvar_Get( "in_device", "", 0 );
if( !in_device->string[0] ) {
- Com_EPrintf( "No input device specified\n" );
+ Com_WPrintf( "No evdev input device specified.\n" );
return qfalse;
}
@@ -156,18 +140,13 @@ static qboolean Evdev_InitMouse( void ) {
fcntl( evdev.fd, F_SETFL, fcntl( evdev.fd, F_GETFL, 0 ) | FNDELAY );
evdev.io = IO_Add( evdev.fd );
- Com_Printf( "Event interface initialized.\n" );
+ Com_Printf( "Evdev interface initialized.\n" );
evdev.initialized = qtrue;
return qtrue;
}
-/*
-===========
-Evdev_GrabMouse
-===========
-*/
-static void Evdev_GrabMouse( grab_t grab ) {
+static void GrabMouse( grab_t grab ) {
if( !evdev.initialized ) {
return;
}
@@ -206,11 +185,11 @@ DI_FillAPI
@@@@@@@@@@@@@@@@@@@
*/
void DI_FillAPI( inputAPI_t *api ) {
- api->Init = Evdev_InitMouse;
- api->Shutdown = Evdev_ShutdownMouse;
- api->Grab = Evdev_GrabMouse;
- api->GetEvents = Evdev_GetMouseEvents;
- api->GetMotion = Evdev_GetMouseMotion;
+ api->Init = InitMouse;
+ api->Shutdown = ShutdownMouse;
+ api->Grab = GrabMouse;
+ api->GetEvents = GetMouseEvents;
+ api->GetMotion = GetMouseMotion;
}
diff --git a/source/io_sleep.c b/source/io_sleep.c
index 313bda4..00e5144 100644
--- a/source/io_sleep.c
+++ b/source/io_sleep.c
@@ -28,7 +28,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
-#include <errno.h>
#endif
static ioentry_t entries[FD_SETSIZE];
diff --git a/source/mvd_client.c b/source/mvd_client.c
index 01ef0f6..86a72c3 100644
--- a/source/mvd_client.c
+++ b/source/mvd_client.c
@@ -77,7 +77,7 @@ typedef struct gtv_s {
unsigned retry_backoff;
// demo related variables
- fileHandle_t demoplayback;
+ qhandle_t demoplayback;
int demoloop, demoskip;
string_entry_t *demohead, *demoentry;
size_t demosize, demopos;
@@ -427,70 +427,98 @@ DEMO PLAYER
static void demo_play_next( gtv_t *gtv, string_entry_t *entry );
-static size_t demo_read_msglen( fileHandle_t f ) {
- uint16_t msglen;
+static ssize_t demo_load_message( qhandle_t f ) {
+ uint16_t us;
+ ssize_t msglen, read;
- if( FS_Read( &msglen, 2, f ) != 2 ) {
- return 0;
+ read = FS_Read( &us, 2, f );
+ if( read != 2 ) {
+ return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
}
- if( !msglen ) {
+
+ if( !us ) {
return 0;
}
- msglen = LittleShort( msglen );
+
+ msglen = LittleShort( us );
if( msglen > MAX_MSGLEN ) {
- return 0;
+ return Q_ERR_INVALID_FORMAT;
}
- return msglen;
+
+ read = FS_Read( msg_read_buffer, msglen, f );
+ if( read != msglen ) {
+ return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
+ }
+
+ return read;
}
-static qboolean demo_skip_map( fileHandle_t f ) {
- size_t msglen;
+static ssize_t demo_skip_map( qhandle_t f ) {
+ ssize_t msglen;
while( 1 ) {
- if( ( msglen = demo_read_msglen( f ) ) == 0 ) {
- return qfalse;
- }
- if( FS_Read( msg_read_buffer, 1, f ) != 1 ) {
- return qfalse;
+ if( ( msglen = demo_load_message( f ) ) <= 0 ) {
+ return msglen;
}
if( msg_read_buffer[0] == mvd_serverdata ) {
break;
}
- if( !FS_Seek( f, msglen - 1 ) ) {
- return qfalse;
- }
}
- if( FS_Read( msg_read_buffer + 1, msglen - 1, f ) != msglen - 1 ) {
- return qfalse;
+ SZ_Init( &msg_read, msg_read_buffer, sizeof( msg_read_buffer ) );
+ msg_read.cursize = msglen;
+
+ return msglen;
+}
+
+static ssize_t demo_read_message( qhandle_t f ) {
+ ssize_t msglen;
+
+ if( ( msglen = demo_load_message( f ) ) <= 0 ) {
+ return msglen;
}
SZ_Init( &msg_read, msg_read_buffer, sizeof( msg_read_buffer ) );
msg_read.cursize = msglen;
- return qtrue;
+ return msglen;
}
-static qboolean demo_read_message( fileHandle_t f ) {
- size_t msglen;
+static ssize_t demo_read_first( qhandle_t f ) {
+ uint32_t magic;
+ ssize_t read;
+ qerror_t ret;
- if( ( msglen = demo_read_msglen( f ) ) == 0 ) {
- return qfalse;
+ // read magic
+ read = FS_Read( &magic, 4, f );
+ if( read != 4 ) {
+ return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
}
- if( FS_Read( msg_read_buffer, msglen, f ) != msglen ) {
- return qfalse;
+ // check for gzip header
+ if( ( ( LittleLong( magic ) & 0xe0ffffff ) == 0x00088b1f ) ) {
+ ret = FS_FilterFile( f );
+ if( ret ) {
+ return ret;
+ }
+ read = FS_Read( &magic, 4, f );
+ if( read != 4 ) {
+ return read < 0 ? read : Q_ERR_UNEXPECTED_EOF;
+ }
+ }
+ if( magic != MVD_MAGIC ) {
+ return Q_ERR_UNKNOWN_FORMAT;
}
- SZ_Init( &msg_read, msg_read_buffer, sizeof( msg_read_buffer ) );
- msg_read.cursize = msglen;
-
- return qtrue;
+ // read the first message
+ read = demo_read_message( f );
+ return read ? read : Q_ERR_UNEXPECTED_EOF;
}
static qboolean demo_read_frame( mvd_t *mvd ) {
gtv_t *gtv = mvd->gtv;
int count;
+ ssize_t ret;
if( mvd->state == MVD_WAITING ) {
return qfalse; // paused by user
@@ -503,13 +531,16 @@ static qboolean demo_read_frame( mvd_t *mvd ) {
gtv->demoskip = 0;
if( count ) {
+ Com_Printf( "[%s] -=- Skipping map%s...\n", gtv->name, count == 1 ? "" : "s" );
do {
- if( !demo_skip_map( gtv->demoplayback ) ) {
+ ret = demo_skip_map( gtv->demoplayback );
+ if( ret <= 0 ) {
goto next;
}
} while( --count );
} else {
- if( !demo_read_message( gtv->demoplayback ) ) {
+ ret = demo_read_message( gtv->demoplayback );
+ if( ret <= 0 ) {
goto next;
}
}
@@ -522,12 +553,15 @@ static qboolean demo_read_frame( mvd_t *mvd ) {
return qtrue;
next:
+ if( ret < 0 ) {
+ gtv_destroyf( gtv, "Couldn't read %s: %s", gtv->demoentry->string, Q_ErrorString( ret ) );
+ }
demo_play_next( gtv, gtv->demoentry->next );
return qtrue;
}
static void demo_play_next( gtv_t *gtv, string_entry_t *entry ) {
- uint32_t magic = 0;
+ ssize_t len, ret;
if( !entry ) {
if( gtv->demoloop ) {
@@ -545,30 +579,15 @@ static void demo_play_next( gtv_t *gtv, string_entry_t *entry ) {
}
// open new file
- FS_FOpenFile( entry->string, &gtv->demoplayback, FS_MODE_READ );
+ len = FS_FOpenFile( entry->string, &gtv->demoplayback, FS_MODE_READ );
if( !gtv->demoplayback ) {
- gtv_destroyf( gtv, "Couldn't reopen %s", entry->string );
- }
-
- // figure out if file is compressed and check magic
- if( FS_Read( &magic, 4, gtv->demoplayback ) != 4 ) {
- gtv_destroyf( gtv, "Couldn't read magic from %s", entry->string );
- }
- if( ( ( LittleLong( magic ) & 0xe0ffffff ) == 0x00088b1f ) ) {
- if( !FS_FilterFile( gtv->demoplayback ) ) {
- gtv_destroyf( gtv, "Couldn't install gzip filter on %s", entry->string );
- }
- if( FS_Read( &magic, 4, gtv->demoplayback ) != 4 ) {
- gtv_destroyf( gtv, "Couldn't read magic from %s", entry->string );
- }
- }
- if( magic != MVD_MAGIC ) {
- gtv_destroyf( gtv, "%s is not a MVD2 file", entry->string );
+ gtv_destroyf( gtv, "Couldn't open %s: %s", entry->string, Q_ErrorString( len ) );
}
// read the first message
- if( !demo_read_message( gtv->demoplayback ) ) {
- gtv_destroyf( gtv, "Couldn't read first message from %s", entry->string );
+ ret = demo_read_first( gtv->demoplayback );
+ if( ret < 0 ) {
+ gtv_destroyf( gtv, "Couldn't read %s: %s", entry->string, Q_ErrorString( ret ) );
}
// create MVD channel
@@ -593,11 +612,13 @@ static void demo_play_next( gtv_t *gtv, string_entry_t *entry ) {
// set channel address
Q_strlcpy( gtv->address, COM_SkipPath( entry->string ), sizeof( gtv->address ) );
- gtv->demosize = FS_GetFileLength( gtv->demoplayback );
- if( gtv->demosize == INVALID_LENGTH ) {
- gtv->demosize = 0;
+ ret = FS_Tell( gtv->demoplayback );
+ if( ret > 0 ) {
+ gtv->demosize = len;
+ gtv->demopos = ret;
+ } else {
+ gtv->demosize = gtv->demopos = 0;
}
- gtv->demopos = FS_Tell( gtv->demoplayback );
}
static void demo_free_playlist( gtv_t *gtv ) {
@@ -1473,7 +1494,7 @@ static void list_generic( void ) {
static void list_recordings( void ) {
mvd_t *mvd;
char buffer[8];
- size_t bytes;
+ ssize_t pos;
Com_Printf(
"id name map size name\n"
@@ -1481,11 +1502,11 @@ static void list_recordings( void ) {
FOR_EACH_MVD( mvd ) {
if( mvd->demorecording ) {
- bytes = FS_Tell( mvd->demorecording );
- if( bytes == INVALID_LENGTH ) {
+ pos = FS_Tell( mvd->demorecording );
+ if( pos < 0 ) {
strcpy( buffer, "???" );
} else {
- Q_FormatFileSize( buffer, bytes, sizeof( buffer ) );
+ Q_FormatFileSize( buffer, pos, sizeof( buffer ) );
}
} else {
strcpy( buffer, "-" );
@@ -1642,13 +1663,12 @@ static void MVD_EmitGamestate( mvd_t *mvd ) {
void MVD_StreamedRecord_f( void ) {
char buffer[MAX_OSPATH];
- fileHandle_t f;
+ qhandle_t f;
mvd_t *mvd;
uint32_t magic;
uint16_t msglen;
- qboolean gzip = qfalse;
+ unsigned mode = FS_MODE_WRITE;
int c;
- size_t len;
while( ( c = Cmd_ParseOptions( o_record ) ) != -1 ) {
switch( c ) {
@@ -1658,7 +1678,7 @@ void MVD_StreamedRecord_f( void ) {
Cmd_PrintHelp( o_record );
return;
case 'z':
- gzip = qtrue;
+ mode |= FS_FLAG_GZIP;
break;
default:
return;
@@ -1685,24 +1705,13 @@ void MVD_StreamedRecord_f( void ) {
//
// open the demo file
//
- len = Q_concat( buffer, sizeof( buffer ), "demos/", cmd_optarg,
- gzip ? ".mvd2.gz" : ".mvd2", NULL );
- if( len >= sizeof( buffer ) ) {
- Com_EPrintf( "Oversize filename specified.\n" );
- return;
- }
-
- FS_FOpenFile( buffer, &f, FS_MODE_WRITE );
+ f = FS_EasyOpenFile( buffer, sizeof( buffer ), mode,
+ "demos/", cmd_optarg, ".mvd2" );
if( !f ) {
- Com_EPrintf( "Couldn't open %s for writing.\n", buffer );
return;
}
-
- Com_Printf( "[%s] Recording into %s\n", mvd->name, buffer );
- if( gzip ) {
- FS_FilterFile( f );
- }
+ Com_Printf( "[%s] Recording into %s\n", mvd->name, buffer );
mvd->demorecording = f;
mvd->demoname = MVD_CopyString( buffer );
@@ -2032,11 +2041,11 @@ static void MVD_Play_f( void ) {
Q_strlcpy( buffer, s + 1, sizeof( buffer ) );
} else {
Q_concat( buffer, sizeof( buffer ), "demos/", s, NULL );
- if( FS_LoadFile( buffer, NULL ) == INVALID_LENGTH ) {
+ if( !FS_FileExists( buffer ) ) {
COM_DefaultExtension( buffer, ".mvd2", sizeof( buffer ) );
}
}
- if( FS_LoadFile( buffer, NULL ) == INVALID_LENGTH ) {
+ if( !FS_FileExists( buffer ) ) {
Com_Printf( "Ignoring non-existent entry: %s\n", buffer );
continue;
}
@@ -2079,6 +2088,10 @@ static void MVD_Play_f( void ) {
// set new playlist
gtv->demohead = head;
+ if( setjmp( mvd_jmpbuf ) ) {
+ return;
+ }
+
demo_play_next( gtv, head );
}
diff --git a/source/mvd_game.c b/source/mvd_game.c
index 3301ab1..12b54d8 100644
--- a/source/mvd_game.c
+++ b/source/mvd_game.c
@@ -1238,6 +1238,7 @@ static void MVD_GameInit( void ) {
unsigned checksum;
bsp_t *bsp;
int i;
+ qerror_t ret;
Com_Printf( "----- MVD_GameInit -----\n" );
@@ -1275,9 +1276,10 @@ static void MVD_GameInit( void ) {
Q_snprintf( buffer, sizeof( buffer ),
"maps/%s.bsp", mvd_default_map->string );
- if( ( bsp = BSP_Load( buffer ) ) == NULL ) {
- Com_WPrintf( "Couldn't load %s for the Waiting Room: %s\n",
- buffer, BSP_GetError() );
+ ret = BSP_Load( buffer, &bsp );
+ if( !bsp ) {
+ Com_EPrintf( "Couldn't load %s for the Waiting Room: %s\n",
+ buffer, Q_ErrorString( ret ) );
Cvar_Reset( mvd_default_map );
strcpy( buffer, "maps/q2dm1.bsp" );
checksum = 80717714;
diff --git a/source/mvd_local.h b/source/mvd_local.h
index bd65055..62042eb 100644
--- a/source/mvd_local.h
+++ b/source/mvd_local.h
@@ -123,8 +123,8 @@ typedef struct mvd_s {
qboolean (*forward_cmd)( mvd_client_t * );
// demo related variables
- fileHandle_t demorecording;
- char *demoname;
+ qhandle_t demorecording;
+ char *demoname;
// delay buffer
fifo_t delay;
diff --git a/source/mvd_parse.c b/source/mvd_parse.c
index a74a63a..3b13a32 100644
--- a/source/mvd_parse.c
+++ b/source/mvd_parse.c
@@ -979,6 +979,7 @@ static void MVD_ParseServerData( mvd_t *mvd, int extrabits ) {
size_t len, maxlen;
char *string;
int i, index;
+ qerror_t ret;
// clear the leftover from previous level
MVD_ClearState( mvd );
@@ -1072,8 +1073,9 @@ static void MVD_ParseServerData( mvd_t *mvd, int extrabits ) {
// load the world model (we are only interesed in visibility info)
Com_Printf( "[%s] -=- Loading %s...\n", mvd->name, string );
- if( !CM_LoadMap( &mvd->cm, string ) ) {
- Com_EPrintf( "[%s] =!= Couldn't load %s: %s\n", mvd->name, string, BSP_GetError() );
+ ret = CM_LoadMap( &mvd->cm, string );
+ if( ret < 0 ) {
+ Com_EPrintf( "[%s] =!= Couldn't load %s: %s\n", mvd->name, string, Q_ErrorString( ret ) );
// continue with null visibility
}
#if USE_MAPCHECKSUM
diff --git a/source/net_common.c b/source/net_common.c
index 7373341..81561fd 100644
--- a/source/net_common.c
+++ b/source/net_common.c
@@ -58,7 +58,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
-#include <errno.h>
#include <arpa/inet.h>
#ifdef __linux__
#include <linux/types.h>
@@ -128,7 +127,7 @@ static const char socketNames[NS_COUNT][8] = { "Client", "Server" };
static SOCKET udp_sockets[NS_COUNT] = { INVALID_SOCKET, INVALID_SOCKET };
static SOCKET tcp_socket = INVALID_SOCKET;
#ifdef _DEBUG
-static fileHandle_t net_logFile;
+static qhandle_t net_logFile;
#endif
// current rate measurement
@@ -310,29 +309,22 @@ static void logfile_close( void ) {
static void logfile_open( void ) {
char buffer[MAX_OSPATH];
- size_t len;
- int mode;
-
- len = Q_concat( buffer, sizeof( buffer ), "logs/",
- net_log_name->string, ".log", NULL );
- if( len >= sizeof( buffer ) ) {
- Com_WPrintf( "Oversize logfile name specified\n" );
- Cvar_Set( "net_log_enable", "0" );
- return;
- }
+ unsigned mode;
+ qhandle_t f;
mode = net_log_enable->integer > 1 ? FS_MODE_APPEND : FS_MODE_WRITE;
if( net_log_flush->integer ) {
mode |= FS_FLUSH_SYNC;
}
- FS_FOpenFile( buffer, &net_logFile, mode );
- if( !net_logFile ) {
- Com_WPrintf( "Couldn't open %s\n", buffer );
+ f = FS_EasyOpenFile( buffer, sizeof( buffer ), mode,
+ "logs/", net_log_name->string, ".log" );
+ if( !f ) {
Cvar_Set( "net_log_enable", "0" );
return;
}
+ net_logFile = f;
Com_Printf( "Logging network packets to %s\n", buffer );
}
diff --git a/source/prompt.c b/source/prompt.c
index 54b9dfa..f106bf4 100644
--- a/source/prompt.c
+++ b/source/prompt.c
@@ -510,7 +510,7 @@ void Prompt_Clear( commandPrompt_t *prompt ) {
}
void Prompt_SaveHistory( commandPrompt_t *prompt, const char *filename, int lines ) {
- fileHandle_t f;
+ qhandle_t f;
char *s;
int i;
@@ -539,9 +539,9 @@ void Prompt_SaveHistory( commandPrompt_t *prompt, const char *filename, int line
void Prompt_LoadHistory( commandPrompt_t *prompt, const char *filename ) {
char buffer[MAX_FIELD_TEXT];
- fileHandle_t f;
+ qhandle_t f;
int i;
- size_t len;
+ ssize_t len;
FS_FOpenFile( filename, &f, FS_MODE_READ|FS_TYPE_REAL|FS_PATH_BASE );
if( !f ) {
diff --git a/source/q_shared.h b/source/q_shared.h
index 541b046..400f9e2 100644
--- a/source/q_shared.h
+++ b/source/q_shared.h
@@ -76,10 +76,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#endif /* !__GNUC__ */
-typedef unsigned char byte;
-typedef enum { qfalse, qtrue } qboolean;
+typedef unsigned char byte;
+typedef enum { qfalse, qtrue } qboolean;
typedef int qhandle_t;
-typedef int fileHandle_t;
+typedef int qerror_t;
#ifndef NULL
#define NULL ((void *)0)
diff --git a/source/r_images.c b/source/r_images.c
index 8468c61..e62ab39 100644
--- a/source/r_images.c
+++ b/source/r_images.c
@@ -33,7 +33,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#if !USE_PNG
#include <setjmp.h>
#endif
-#include <stdio.h>
#include <jpeglib.h>
#endif
@@ -57,12 +56,13 @@ PCX LOADING
IMG_LoadPCX
==============
*/
-qboolean IMG_LoadPCX( const char *filename, byte **pic, byte *palette, int *width, int *height ) {
+qerror_t IMG_LoadPCX( const char *filename, byte **pic, byte *palette, int *width, int *height ) {
byte *raw, *end;
dpcx_t *pcx;
size_t len, x, y, w, h;
int dataByte, runLength;
byte *out, *pix;
+ qerror_t ret;
if( !filename ) {
Com_Error( ERR_FATAL, "LoadPCX: NULL" );
@@ -76,23 +76,26 @@ qboolean IMG_LoadPCX( const char *filename, byte **pic, byte *palette, int *widt
//
len = FS_LoadFile( filename, (void **)&pcx );
if( !pcx ) {
- return qfalse;
- }
- if( len < sizeof( *pcx ) ) {
- Com_WPrintf( "LoadPCX: %s: file too short\n", filename );
- goto fail2;
+ return len;
}
//
// parse the PCX file
//
+ if( len < sizeof( *pcx ) ) {
+ ret = Q_ERR_FILE_TOO_SMALL;
+ goto fail2;
+ }
+
+ if( pcx->manufacturer != 0x0a || pcx->version != 5 ) {
+ ret = Q_ERR_UNKNOWN_FORMAT;
+ goto fail2;
+ }
+
w = LittleShort( pcx->xmax ) + 1;
h = LittleShort( pcx->ymax ) + 1;
- if( pcx->manufacturer != 0x0a || pcx->version != 5
- || pcx->encoding != 1 || pcx->bits_per_pixel != 8
- || w > 640 || h > 480 )
- {
- Com_WPrintf( "LoadPCX: %s: unsupported format\n", filename );
+ if( pcx->encoding != 1 || pcx->bits_per_pixel != 8 || w > 640 || h > 480 ) {
+ ret = Q_ERR_INVALID_FORMAT;
goto fail2;
}
@@ -101,7 +104,7 @@ qboolean IMG_LoadPCX( const char *filename, byte **pic, byte *palette, int *widt
//
if( palette ) {
if( len < 768 ) {
- Com_WPrintf( "LoadPCX: %s: palette too short\n", filename );
+ ret = Q_ERR_FILE_TOO_SMALL;
goto fail2;
}
memcpy( palette, ( byte * )pcx + len - 768, 768 );
@@ -119,7 +122,7 @@ qboolean IMG_LoadPCX( const char *filename, byte **pic, byte *palette, int *widt
for( y = 0; y < h; y++, pix += w ) {
for( x = 0; x < w; ) {
if( raw >= end ) {
- Com_WPrintf( "LoadPCX: %s: read past end of file\n", filename );
+ ret = Q_ERR_BAD_EXTENT;
goto fail1;
}
dataByte = *raw++;
@@ -127,11 +130,11 @@ qboolean IMG_LoadPCX( const char *filename, byte **pic, byte *palette, int *widt
if( ( dataByte & 0xC0 ) == 0xC0 ) {
runLength = dataByte & 0x3F;
if( x + runLength > w ) {
- Com_WPrintf( "LoadPCX: %s: run length overrun\n", filename );
+ ret = Q_ERR_BAD_RLE_PACKET;
goto fail1;
}
if( raw >= end ) {
- Com_WPrintf( "LoadPCX: %s: read past end of file\n", filename );
+ ret = Q_ERR_BAD_RLE_PACKET;
goto fail1;
}
dataByte = *raw++;
@@ -153,28 +156,29 @@ qboolean IMG_LoadPCX( const char *filename, byte **pic, byte *palette, int *widt
*height = h;
FS_FreeFile( pcx );
- return qtrue;
+ return Q_ERR_SUCCESS;
fail1:
IMG_FreePixels( out );
fail2:
FS_FreeFile( pcx );
- return qfalse;
+ return ret;
}
+#if 0
/*
==============
-IMG_WritePCX
+IMG_SavePCX
==============
*/
-qboolean IMG_WritePCX( const char *filename, const byte *data, int width,
+qerror_t IMG_SavePCX( const char *filename, const byte *data, int width,
int height, int rowbytes, byte *palette )
{
- int i, j, length;
+ int i, j;
+ size_t len;
dpcx_t *pcx;
byte *pack;
- qboolean ret = qfalse;
- fileHandle_t f;
+ qerror_t ret;
pcx = FS_AllocTempMem( width * height * 2 + 1000 );
pcx->manufacturer = 0x0a; // PCX id
@@ -209,247 +213,237 @@ qboolean IMG_WritePCX( const char *filename, const byte *data, int width,
*pack++ = 0x0c; // palette ID byte
for( i = 0; i < 768; i++ )
*pack++ = *palette++;
-
+
// write output file
- FS_FOpenFile( filename, &f, FS_MODE_WRITE );
- if( !f ) {
- goto fail;
- }
-
- length = pack - ( byte * )pcx;
- if( FS_Write( pcx, length, f ) == length ) {
- ret = qtrue;
- }
-
- FS_FCloseFile( f );
-
-fail:
+ len = pack - ( byte * )pcx;
+ ret = FS_WriteFile( filename, pcx, len );
FS_FreeFile( pcx );
+
return ret;
-}
+}
+#endif
#if USE_TGA
/*
=========================================================
-TARGA LOADING
+TARGA IMAGES
=========================================================
*/
+#define TARGA_HEADER_SIZE 18
+
#define TGA_DECODE( x ) \
- static qboolean tga_decode_##x( byte *data, byte *pixels, \
- int columns, int rows, byte *maxp )
+ static qerror_t tga_decode_##x( byte *in, byte *out, int cols, int rows, byte *max_in )
-typedef qboolean (*tga_decode_t)( byte *, byte *, int, int, byte * );
+typedef qerror_t (*tga_decode_t)( byte *, byte *, int, int, byte * );
TGA_DECODE( bgr ) {
int col, row;
- uint32_t *pixbuf;
+ byte *out_row;
for( row = rows - 1; row >= 0; row-- ) {
- pixbuf = ( uint32_t * )pixels + row * columns;
-
- for( col = 0; col < columns; col++ ) {
- *pixbuf++ = MakeColor( data[2], data[1], data[0], 255 );
- data += 3;
+ out_row = out + row * cols * 4;
+ for( col = 0; col < cols; col++, out_row += 4, in += 3 ) {
+ out_row[0] = in[2];
+ out_row[1] = in[1];
+ out_row[2] = in[0];
+ out_row[3] = 255;
}
}
- return qtrue;
+ return Q_ERR_SUCCESS;
}
TGA_DECODE( bgra ) {
int col, row;
- uint32_t *pixbuf;
+ byte *out_row;
for( row = rows - 1; row >= 0; row-- ) {
- pixbuf = ( uint32_t * )pixels + row * columns;
-
- for( col = 0; col < columns; col++ ) {
- *pixbuf++ = MakeColor( data[2], data[1], data[0], data[3] );
- data += 4;
+ out_row = out + row * cols * 4;
+ for( col = 0; col < cols; col++, out_row += 4, in += 4 ) {
+ out_row[0] = in[2];
+ out_row[1] = in[1];
+ out_row[2] = in[0];
+ out_row[3] = in[3];
}
}
- return qtrue;
+ return Q_ERR_SUCCESS;
}
TGA_DECODE( bgr_flip ) {
- int count;
- uint32_t *pixbuf;
+ int i, count = rows * cols;
- pixbuf = ( uint32_t * )pixels;
- count = rows * columns;
- do {
- *pixbuf++ = MakeColor( data[2], data[1], data[0], 255 );
- data += 3;
- } while( --count );
+ for( i = 0; i < count; i++, out += 4, in += 3 ) {
+ out[0] = in[2];
+ out[1] = in[1];
+ out[2] = in[0];
+ out[3] = 255;
+ }
- return qtrue;
+ return Q_ERR_SUCCESS;
}
TGA_DECODE( bgra_flip ) {
- int count;
- uint32_t *pixbuf;
+ int i, count = rows * cols;
- pixbuf = ( uint32_t * )pixels;
- count = rows * columns;
- do {
- *pixbuf++ = MakeColor( data[2], data[1], data[0], data[3] );
- data += 4;
- } while( --count );
+ for( i = 0; i < count; i++, out += 4, in += 3 ) {
+ out[0] = in[2];
+ out[1] = in[1];
+ out[2] = in[0];
+ out[3] = in[3];
+ }
- return qtrue;
+ return Q_ERR_SUCCESS;
}
TGA_DECODE( bgr_rle ) {
int col, row;
- uint32_t *pixbuf, color;
- byte packetHeader, packetSize;
+ byte *out_row;
+ uint32_t color;
+ unsigned packet_header, packet_size;
int j;
for( row = rows - 1; row >= 0; row-- ) {
- pixbuf = ( uint32_t * )pixels + row * columns;
+ out_row = out + row * cols * 4;
- for( col = 0; col < columns; ) {
- packetHeader = *data++;
- packetSize = 1 + ( packetHeader & 0x7f );
+ for( col = 0; col < cols; ) {
+ packet_header = *in++;
+ packet_size = 1 + ( packet_header & 0x7f );
- if( packetHeader & 0x80 ) {
- /* run-length packet */
- if( data + 3 > maxp ) {
- return qfalse;
+ if( packet_header & 0x80 ) {
+ // run-length packet
+ if( in + 3 > max_in ) {
+ return Q_ERR_BAD_RLE_PACKET;
}
- color = MakeColor( data[2], data[1], data[0], 255 );
- data += 3;
- for( j = 0; j < packetSize; j++ ) {
- *pixbuf++ = color;
-
- col++;
- if( col == columns ) {
- /* run spans across rows */
+ color = MakeColor( in[2], in[1], in[0], 255 );
+ in += 3;
+ for( j = 0; j < packet_size; j++ ) {
+ *(uint32_t *)out_row = color;
+ out_row += 4;
+
+ if( ++col == cols ) {
+ // run spans across rows
col = 0;
-
if( row > 0 )
row--;
else
- goto breakOut;
-
- pixbuf = ( uint32_t * )pixels + row * columns;
+ goto break_out;
+ out_row = out + row * cols * 4;
}
}
} else {
- /* non run-length packet */
- if( data + 3 * packetSize > maxp ) {
- return qfalse;
+ // non run-length packet
+ if( in + 3 * packet_size > max_in ) {
+ return Q_ERR_BAD_RLE_PACKET;
}
- for( j = 0; j < packetSize; j++ ) {
- *pixbuf++ = MakeColor( data[2], data[1], data[0], 255 );
- data += 3;
-
- col++;
- if( col == columns ) {
- /* run spans across rows */
+ for( j = 0; j < packet_size; j++ ) {
+ out_row[0] = in[2];
+ out_row[1] = in[1];
+ out_row[2] = in[0];
+ out_row[3] = 255;
+ out_row += 4;
+ in += 3;
+
+ if( ++col == cols ) {
+ // run spans across rows
col = 0;
if( row > 0 )
row--;
else
- goto breakOut;
- pixbuf = ( uint32_t * )pixels + row * columns;
+ goto break_out;
+ out_row = out + row * cols * 4;
}
}
}
}
-breakOut: ;
-
}
- return qtrue;
-
+break_out:
+ return Q_ERR_SUCCESS;
}
TGA_DECODE( bgra_rle ) {
int col, row;
- uint32_t *pixbuf, color;
- byte packetHeader, packetSize;
+ byte *out_row;
+ uint32_t color;
+ unsigned packet_header, packet_size;
int j;
for( row = rows - 1; row >= 0; row-- ) {
- pixbuf = ( uint32_t * )pixels + row * columns;
+ out_row = out + row * cols * 4;
- for( col = 0; col < columns; ) {
- packetHeader = *data++;
- packetSize = 1 + ( packetHeader & 0x7f );
+ for( col = 0; col < cols; ) {
+ packet_header = *in++;
+ packet_size = 1 + ( packet_header & 0x7f );
- if( packetHeader & 0x80 ) {
- /* run-length packet */
- if( data + 4 > maxp ) {
- return qfalse;
+ if( packet_header & 0x80 ) {
+ // run-length packet
+ if( in + 4 > max_in ) {
+ return Q_ERR_BAD_RLE_PACKET;
}
- color = MakeColor( data[2], data[1], data[0], data[3] );
- data += 4;
- for( j = 0; j < packetSize; j++ ) {
- *pixbuf++ = color;
-
- col++;
- if( col == columns ) {
- /* run spans across rows */
+ color = MakeColor( in[2], in[1], in[0], in[3] );
+ in += 4;
+ for( j = 0; j < packet_size; j++ ) {
+ *(uint32_t *)out_row = color;
+ out_row += 4;
+
+ if( ++col == cols ) {
+ // run spans across rows
col = 0;
-
if( row > 0 )
row--;
else
- goto breakOut;
-
- pixbuf = ( uint32_t * )pixels + row * columns;
+ goto break_out;
+ out_row = out + row * cols * 4;
}
}
} else {
- /* non run-length packet */
- if( data + 4 * packetSize > maxp ) {
- return qfalse;
+ // non run-length packet
+ if( in + 4 * packet_size > max_in ) {
+ return Q_ERR_BAD_RLE_PACKET;
}
- for( j = 0; j < packetSize; j++ ) {
- *pixbuf++ = MakeColor( data[2], data[1], data[0], data[3] );
- data += 4;
-
- col++;
- if( col == columns ) {
- /* run spans across rows */
+ for( j = 0; j < packet_size; j++ ) {
+ out_row[0] = in[2];
+ out_row[1] = in[1];
+ out_row[2] = in[0];
+ out_row[3] = in[3];
+ out_row += 4;
+ in += 4;
+
+ if( ++col == cols ) {
+ // run spans across rows
col = 0;
if( row > 0 )
row--;
else
- goto breakOut;
- pixbuf = ( uint32_t * )pixels + row * columns;
+ goto break_out;
+ out_row = out + row * cols * 4;
}
}
}
}
-breakOut: ;
-
}
- return qtrue;
-
+break_out:
+ return Q_ERR_SUCCESS;
}
-#define TARGA_HEADER_SIZE 18
-
/*
=============
-LoadTGA
+IMG_LoadTGA
=============
*/
-void IMG_LoadTGA( const char *filename, byte **pic, int *width, int *height ) {
+qerror_t IMG_LoadTGA( const char *filename, byte **pic, int *width, int *height ) {
byte *buffer;
- size_t length;
+ size_t length, offset;
byte *pixels;
- int offset, w, h;
+ unsigned w, h, id_length, image_type, pixel_size, attributes, bpp;
tga_decode_t decode;
- int id_length, image_type, pixel_size, attributes, bpp;
+ qerror_t ret;
if( !filename || !pic ) {
Com_Error( ERR_FATAL, "LoadTGA: NULL" );
@@ -462,11 +456,11 @@ void IMG_LoadTGA( const char *filename, byte **pic, int *width, int *height ) {
//
length = FS_LoadFile( filename, ( void ** )&buffer );
if( !buffer ) {
- return;
+ return length;
}
if( length < TARGA_HEADER_SIZE ) {
- Com_WPrintf( "LoadTGA: %s: file too small\n", filename );
+ ret = Q_ERR_FILE_TOO_SMALL;
goto finish;
}
@@ -480,7 +474,7 @@ void IMG_LoadTGA( const char *filename, byte **pic, int *width, int *height ) {
// skip TARGA image comment
offset = TARGA_HEADER_SIZE + id_length;
if( offset + 4 > length ) {
- Com_WPrintf( "LoadTGA: %s: offset out of range\n", filename );
+ ret = Q_ERR_BAD_EXTENT;
goto finish;
}
@@ -489,21 +483,20 @@ void IMG_LoadTGA( const char *filename, byte **pic, int *width, int *height ) {
} else if( pixel_size == 24 ) {
bpp = 3;
} else {
- Com_WPrintf( "LoadTGA: %s: only 32 and 24 bit targa RGB "
- "images supported, this one is %d bit\n",
- filename, pixel_size );
+ Com_DPrintf( "%s: %s: only 32 and 24 bit targa RGB images supported\n", __func__, filename );
+ ret = Q_ERR_INVALID_FORMAT;
goto finish;
}
if( w < 1 || h < 1 || w > MAX_TEXTURE_SIZE || h > MAX_TEXTURE_SIZE ) {
- Com_WPrintf( "LoadTGA: %s: bad dimensions: %dx%d\n",
- filename, w, h );
+ Com_DPrintf( "%s: %s: invalid image dimensions\n", __func__, filename );
+ ret = Q_ERR_INVALID_FORMAT;
goto finish;
}
if( image_type == 2 ) {
if( offset + w * h * bpp > length ) {
- Com_WPrintf( "LoadTGA: %s: malformed targa image\n", filename );
+ ret = Q_ERR_BAD_EXTENT;
goto finish;
}
if( attributes & 32 ) {
@@ -521,8 +514,8 @@ void IMG_LoadTGA( const char *filename, byte **pic, int *width, int *height ) {
}
} else if( image_type == 10 ) {
if( attributes & 32 ) {
- Com_WPrintf( "LoadTGA: %s: vertically flipped, RLE encoded "
- "images are not supported\n", filename );
+ Com_DPrintf( "%s: %s: vertically flipped, RLE encoded images are not supported\n", __func__, filename );
+ ret = Q_ERR_INVALID_FORMAT;
goto finish;
}
if( pixel_size == 32 ) {
@@ -531,47 +524,37 @@ void IMG_LoadTGA( const char *filename, byte **pic, int *width, int *height ) {
decode = tga_decode_bgr_rle;
}
} else {
- Com_WPrintf( "LoadTGA: %s: only type 2 and 10 targa RGB "
- "images supported, this one is %d\n",
- filename, image_type );
+ Com_DPrintf( "%s: %s: only type 2 and 10 targa RGB images supported\n", __func__, filename );
+ ret = Q_ERR_INVALID_FORMAT;
goto finish;
}
pixels = IMG_AllocPixels( w * h * 4 );
- if( decode( buffer + offset, pixels, w, h, buffer + length ) ) {
- *pic = pixels;
- *width = w;
- *height = h;
- } else {
+ ret = decode( buffer + offset, pixels, w, h, buffer + length );
+ if( ret < 0 ) {
IMG_FreePixels( pixels );
+ goto finish;
}
+
+ *pic = pixels;
+ *width = w;
+ *height = h;
+
finish:
FS_FreeFile( buffer );
+ return ret;
}
/*
-=========================================================
-
-TARGA WRITING
-
-=========================================================
-*/
-
-/*
=================
-IMG_WriteTGA
+IMG_SaveTGA
=================
*/
-qboolean IMG_WriteTGA( const char *filename, const byte *bgr, int width, int height ) {
- int length;
- fileHandle_t f;
+qerror_t IMG_SaveTGA( qhandle_t f, const char *filename, const byte *bgr, int width, int height, int unused ) {
+ size_t len;
byte header[TARGA_HEADER_SIZE];
+ ssize_t ret;
- FS_FOpenFile( filename, &f, FS_MODE_WRITE );
- if( !f ) {
- return qfalse;
- }
-
memset( &header, 0, sizeof( header ) );
header[ 2] = 2; // uncompressed type
header[12] = width & 255;
@@ -580,21 +563,18 @@ qboolean IMG_WriteTGA( const char *filename, const byte *bgr, int width, int hei
header[15] = height >> 8;
header[16] = 24; // pixel size
- if( FS_Write( &header, sizeof( header ), f ) != sizeof( header ) ) {
- goto fail;
+ ret = FS_Write( &header, sizeof( header ), f );
+ if( ret < 0 ) {
+ return ret;
}
- length = width * height * 3;
- if( FS_Write( bgr, length, f ) != length ) {
- goto fail;
+ len = width * height * 3;
+ ret = FS_Write( bgr, len, f );
+ if( ret < 0 ) {
+ return ret;
}
-
- FS_FCloseFile( f );
- return qtrue;
-
-fail:
- FS_FCloseFile( f );
- return qfalse;
+
+ return Q_ERR_SUCCESS;
}
#endif // USE_TGA
@@ -602,7 +582,7 @@ fail:
/*
=========================================================
-JPEG LOADING
+JPEG IMAGES
=========================================================
*/
@@ -613,41 +593,43 @@ typedef struct my_error_mgr {
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
const char *filename;
+ qerror_t error;
} *my_error_ptr;
METHODDEF( void )my_output_message( j_common_ptr cinfo ) {
char buffer[JMSG_LENGTH_MAX];
- my_error_ptr myerr = ( my_error_ptr )cinfo->err;
+ my_error_ptr jerr = ( my_error_ptr )cinfo->err;
(*cinfo->err->format_message)( cinfo, buffer );
- Com_WPrintf( "LoadJPG: %s: %s\n", myerr->filename, buffer );
+ Com_EPrintf( "libjpeg: %s: %s\n", jerr->filename, buffer );
}
METHODDEF( void )my_error_exit( j_common_ptr cinfo ) {
- my_error_ptr myerr = ( my_error_ptr )cinfo->err;
+ my_error_ptr jerr = ( my_error_ptr )cinfo->err;
(*cinfo->err->output_message)( cinfo );
- longjmp( myerr->setjmp_buffer, 1 );
+ jerr->error = Q_ERR_LIBRARY_ERROR;
+ longjmp( jerr->setjmp_buffer, 1 );
}
-
METHODDEF( void )mem_init_source( j_decompress_ptr cinfo ) { }
METHODDEF( boolean )mem_fill_input_buffer( j_decompress_ptr cinfo ) {
my_error_ptr jerr = ( my_error_ptr )cinfo->err;
+ jerr->error = Q_ERR_FILE_TOO_SMALL;
longjmp( jerr->setjmp_buffer, 1 );
return TRUE;
}
-
METHODDEF( void )mem_skip_input_data( j_decompress_ptr cinfo, long num_bytes ) {
struct jpeg_source_mgr *src = cinfo->src;
my_error_ptr jerr = ( my_error_ptr )cinfo->err;
if( src->bytes_in_buffer < num_bytes ) {
+ jerr->error = Q_ERR_FILE_TOO_SMALL;
longjmp( jerr->setjmp_buffer, 1 );
}
@@ -657,8 +639,7 @@ METHODDEF( void )mem_skip_input_data( j_decompress_ptr cinfo, long num_bytes ) {
METHODDEF( void )mem_term_source( j_decompress_ptr cinfo ) { }
-
-METHODDEF( void )jpeg_mem_src( j_decompress_ptr cinfo, byte *data, size_t size ) {
+METHODDEF( void )my_mem_src( j_decompress_ptr cinfo, byte *data, size_t size ) {
cinfo->src = ( struct jpeg_source_mgr * )(*cinfo->mem->alloc_small)(
( j_common_ptr )cinfo, JPOOL_PERMANENT, sizeof( struct jpeg_source_mgr ) );
@@ -673,20 +654,20 @@ METHODDEF( void )jpeg_mem_src( j_decompress_ptr cinfo, byte *data, size_t size )
/*
=================
-LoadJPG
+IMG_LoadJPG
=================
*/
-void IMG_LoadJPG( const char *filename, byte **pic, int *width, int *height ) {
+qerror_t IMG_LoadJPG( const char *filename, byte **pic, int *width, int *height ) {
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
- JSAMPARRAY buffer;
- int row_stride;
+ JSAMPROW row_pointer;
+ byte buffer[MAX_TEXTURE_SIZE*3];
byte *rawdata;
size_t rawlength;
- byte *pixels;
- byte *src;
- uint32_t *dst;
+ byte *volatile pixels;
+ byte *in, *out;
int i;
+ qerror_t ret;
if( !filename || !pic ) {
Com_Error( ERR_FATAL, "LoadJPG: NULL" );
@@ -696,88 +677,102 @@ void IMG_LoadJPG( const char *filename, byte **pic, int *width, int *height ) {
rawlength = FS_LoadFile( filename, ( void ** )&rawdata );
if( !rawdata ) {
- return;
+ return rawlength;
}
cinfo.err = jpeg_std_error( &jerr.pub );
jerr.pub.error_exit = my_error_exit;
jerr.pub.output_message = my_output_message;
jerr.filename = filename;
+ jerr.error = Q_ERR_FAILURE;
jpeg_create_decompress( &cinfo );
-
+
if( setjmp( jerr.setjmp_buffer ) ) {
- jpeg_destroy_decompress( &cinfo );
- if( pixels ) {
- IMG_FreePixels( pixels );
- }
- FS_FreeFile( rawdata );
- return;
+ IMG_FreePixels( pixels );
+ ret = jerr.error;
+ goto fail;
}
- jpeg_mem_src( &cinfo, rawdata, rawlength );
+ my_mem_src( &cinfo, rawdata, rawlength );
jpeg_read_header( &cinfo, TRUE );
- jpeg_start_decompress( &cinfo );
- if( cinfo.output_components != 3 /*&& cinfo.output_components != 4*/ ) {
- Com_WPrintf( "LoadJPG: %s: unsupported number of color components: %i\n",
- filename, cinfo.output_components );
- jpeg_destroy_decompress( &cinfo );
- FS_FreeFile( rawdata );
- return;
+ if( cinfo.jpeg_color_space != JCS_RGB && cinfo.jpeg_color_space != JCS_GRAYSCALE ) {
+ Com_DPrintf( "%s: %s: invalid image color space\n", __func__, filename );
+ ret = Q_ERR_INVALID_FORMAT;
+ goto fail;
}
- *width = cinfo.output_width;
- *height = cinfo.output_height;
+ jpeg_start_decompress( &cinfo );
+
+ if( cinfo.output_components != 3 && cinfo.output_components != 1 ) {
+ Com_DPrintf( "%s: %s: invalid number of color components\n", __func__, filename );
+ ret = Q_ERR_INVALID_FORMAT;
+ goto fail;
+ }
- pixels = IMG_AllocPixels( cinfo.output_width * cinfo.output_height * 4 );
+ if( cinfo.output_width > MAX_TEXTURE_SIZE || cinfo.output_height > MAX_TEXTURE_SIZE ) {
+ Com_DPrintf( "%s: %s: invalid image dimensions\n", __func__, filename );
+ ret = Q_ERR_INVALID_FORMAT;
+ goto fail;
+ }
- row_stride = cinfo.output_width * cinfo.output_components;
+ pixels = out = IMG_AllocPixels( cinfo.output_height * cinfo.output_width * 4 );
+ row_pointer = ( JSAMPROW )buffer;
- buffer = (*cinfo.mem->alloc_sarray)( ( j_common_ptr )&cinfo, JPOOL_IMAGE, row_stride, 1 );
+ if( cinfo.output_components == 3 ) {
+ while( cinfo.output_scanline < cinfo.output_height ) {
+ jpeg_read_scanlines( &cinfo, &row_pointer, 1 );
- dst = ( uint32_t * )pixels;
- while( cinfo.output_scanline < cinfo.output_height ) {
- jpeg_read_scanlines( &cinfo, buffer, 1 );
+ in = buffer;
+ for( i = 0; i < cinfo.output_width; i++, out += 4, in += 3 ) {
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = 255;
+ }
+ }
+ } else {
+ while( cinfo.output_scanline < cinfo.output_height ) {
+ jpeg_read_scanlines( &cinfo, &row_pointer, 1 );
- src = ( byte * )buffer[0];
- for( i = 0; i < cinfo.output_width; i++, src += 3 ) {
- *dst++ = MakeColor( src[0], src[1], src[2], 255 );
+ in = buffer;
+ for( i = 0; i < cinfo.output_width; i++, out += 4, in += 1 ) {
+ out[0] = out[1] = out[2] = in[0];
+ out[3] = 255;
+ }
}
}
- jpeg_finish_decompress( &cinfo );
- jpeg_destroy_decompress( &cinfo );
+ *width = cinfo.output_width;
+ *height = cinfo.output_height;
- FS_FreeFile( rawdata );
+ jpeg_finish_decompress( &cinfo );
*pic = pixels;
+ ret = Q_ERR_SUCCESS;
+fail:
+ jpeg_destroy_decompress( &cinfo );
+ FS_FreeFile( rawdata );
+ return ret;
}
-/*
-=========================================================
-
-JPEG WRITING
-
-=========================================================
-*/
-
-#define OUTPUT_BUF_SIZE 4096
+#define OUTPUT_BUF_SIZE 0x10000 // 64 KiB
typedef struct my_destination_mgr {
- struct jpeg_destination_mgr pub; /* public fields */
+ struct jpeg_destination_mgr pub;
- fileHandle_t hFile; /* target stream */
- JOCTET *buffer; /* start of buffer */
+ qhandle_t f;
+ JOCTET *buffer;
} *my_dest_ptr;
-
METHODDEF( void ) vfs_init_destination( j_compress_ptr cinfo ) {
my_dest_ptr dest = ( my_dest_ptr )cinfo->dest;
- /* Allocate the output buffer --- it will be released when done with image */
- dest->buffer = ( JOCTET * )(*cinfo->mem->alloc_small)( ( j_common_ptr )cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * sizeof( JOCTET ) );
+ // Allocate the output buffer --- it will be released when done with image
+ dest->buffer = ( JOCTET * )(*cinfo->mem->alloc_small)
+ ( ( j_common_ptr )cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * sizeof( JOCTET ) );
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
@@ -786,8 +781,11 @@ METHODDEF( void ) vfs_init_destination( j_compress_ptr cinfo ) {
METHODDEF( boolean ) vfs_empty_output_buffer( j_compress_ptr cinfo ) {
my_dest_ptr dest = ( my_dest_ptr )cinfo->dest;
my_error_ptr jerr = ( my_error_ptr )cinfo->err;
+ ssize_t ret;
- if( FS_Write( dest->buffer, OUTPUT_BUF_SIZE, dest->hFile ) != OUTPUT_BUF_SIZE ) {
+ ret = FS_Write( dest->buffer, OUTPUT_BUF_SIZE, dest->f );
+ if( ret != OUTPUT_BUF_SIZE ) {
+ jerr->error = ret < 0 ? ret : Q_ERR_FAILURE;
longjmp( jerr->setjmp_buffer, 1 );
}
@@ -795,97 +793,96 @@ METHODDEF( boolean ) vfs_empty_output_buffer( j_compress_ptr cinfo ) {
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
return TRUE;
-
}
METHODDEF( void ) vfs_term_destination( j_compress_ptr cinfo ) {
my_dest_ptr dest = ( my_dest_ptr )cinfo->dest;
my_error_ptr jerr = ( my_error_ptr )cinfo->err;
- int remaining = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
+ size_t remaining = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
+ ssize_t ret;
- /* Write any data remaining in the buffer */
+ // Write any data remaining in the buffer
if( remaining > 0 ) {
- if( FS_Write( dest->buffer, remaining, dest->hFile ) != remaining ) {
+ ret = FS_Write( dest->buffer, remaining, dest->f );
+ if( ret != remaining ) {
+ jerr->error = ret < 0 ? ret : Q_ERR_FAILURE;
longjmp( jerr->setjmp_buffer, 1 );
}
}
-
}
-
-METHODDEF( void ) jpeg_vfs_dst( j_compress_ptr cinfo, fileHandle_t hFile ) {
+METHODDEF( void ) my_vfs_dst( j_compress_ptr cinfo, qhandle_t f ) {
my_dest_ptr dest;
- dest = ( my_dest_ptr )(*cinfo->mem->alloc_small)( ( j_common_ptr )cinfo, JPOOL_PERMANENT, sizeof( struct my_destination_mgr ) );
+ dest = ( my_dest_ptr )(*cinfo->mem->alloc_small)
+ ( ( j_common_ptr )cinfo, JPOOL_PERMANENT, sizeof( struct my_destination_mgr ) );
cinfo->dest = &dest->pub;
dest->pub.init_destination = vfs_init_destination;
dest->pub.empty_output_buffer = vfs_empty_output_buffer;
dest->pub.term_destination = vfs_term_destination;
- dest->hFile = hFile;
-
+ dest->f = f;
}
/*
=================
-IMG_WriteJPG
+IMG_SaveJPG
=================
*/
-qboolean IMG_WriteJPG( const char *filename, const byte *rgb, int width, int height, int quality ) {
+qerror_t IMG_SaveJPG( qhandle_t f, const char *filename, const byte *rgb, int width, int height, int quality ) {
struct jpeg_compress_struct cinfo;
struct my_error_mgr jerr;
- fileHandle_t hFile;
- JSAMPROW row_pointer[1];
+ volatile JSAMPARRAY row_pointers;
int row_stride;
-
- FS_FOpenFile( filename, &hFile, FS_MODE_WRITE );
- if( !hFile ) {
- Com_DPrintf( "WriteJPG: %s: couldn't create file\n", filename );
- return qfalse;
- }
+ qerror_t ret;
+ int i;
cinfo.err = jpeg_std_error( &jerr.pub );
jerr.pub.error_exit = my_error_exit;
+ jerr.filename = filename;
+ jerr.error = Q_ERR_FAILURE;
+
+ row_pointers = NULL;
if( setjmp( jerr.setjmp_buffer ) ) {
- Com_DPrintf( "WriteJPG: %s: JPEGLIB signaled an error\n", filename );
- jpeg_destroy_compress( &cinfo );
- FS_FCloseFile( hFile );
- return qfalse;
+ ret = jerr.error;
+ goto fail;
}
jpeg_create_compress( &cinfo );
- jpeg_vfs_dst( &cinfo, hFile );
+ my_vfs_dst( &cinfo, f );
- cinfo.image_width = width; // image width and height, in pixels
+ cinfo.image_width = width; // image width and height, in pixels
cinfo.image_height = height;
- cinfo.input_components = 3; // # of color components per pixel
- cinfo.in_color_space = JCS_RGB; // colorspace of input image
-
- clamp( quality, 0, 100 );
+ cinfo.input_components = 3; // # of color components per pixel
+ cinfo.in_color_space = JCS_RGB; // colorspace of input image
jpeg_set_defaults( &cinfo );
- jpeg_set_quality( &cinfo, quality, TRUE );
+ jpeg_set_quality( &cinfo, clamp( quality, 0, 100 ), TRUE );
jpeg_start_compress( &cinfo, TRUE );
+ row_pointers = FS_AllocTempMem( sizeof( JSAMPROW ) * height );
row_stride = width * 3; // JSAMPLEs per row in image_buffer
- while( cinfo.next_scanline < cinfo.image_height ) {
- row_pointer[0] = ( byte * )( &rgb[( cinfo.image_height - cinfo.next_scanline - 1 ) * row_stride] );
- jpeg_write_scanlines( &cinfo, row_pointer, 1 );
+ for( i = 0; i < height; i++ ) {
+ row_pointers[i] = ( JSAMPROW )( rgb + ( height - i - 1 ) * row_stride );
}
+ jpeg_write_scanlines( &cinfo, row_pointers, height );
+
jpeg_finish_compress( &cinfo );
- FS_FCloseFile( hFile );
- jpeg_destroy_compress( &cinfo );
+ ret = Q_ERR_SUCCESS;
- return qtrue;
+fail:
+ FS_FreeFile( row_pointers );
+ jpeg_destroy_compress( &cinfo );
+ return ret;
}
-#endif /* USE_JPG */
+#endif // USE_JPG
#if USE_PNG
@@ -893,55 +890,67 @@ qboolean IMG_WriteJPG( const char *filename, const byte *rgb, int width, int hei
/*
=========================================================
-PNG LOADING
+PNG IMAGES
=========================================================
*/
-struct pngReadStruct {
- byte *data;
- byte *maxp;
-};
+typedef struct {
+ png_bytep next_in;
+ png_size_t avail_in;
+} my_png_io;
-static void QDECL png_vfs_read_fn( png_structp png_ptr, png_bytep buf, png_size_t size ) {
- struct pngReadStruct *r = png_get_io_ptr( png_ptr );
+typedef struct {
+ png_const_charp filename;
+ qerror_t error;
+} my_png_error;
- if( r->data + size > r->maxp ) {
+static void my_png_read_fn( png_structp png_ptr, png_bytep buf, png_size_t size ) {
+ my_png_io *io = png_get_io_ptr( png_ptr );
+
+ if( size > io->avail_in ) {
+ my_png_error *err = png_get_error_ptr( png_ptr );
+ err->error = Q_ERR_FILE_TOO_SMALL;
png_error( png_ptr, "read error" );
} else {
- memcpy( buf, r->data, size );
- r->data += size;
+ memcpy( buf, io->next_in, size );
+ io->next_in += size;
+ io->avail_in -= size;
}
}
-static void QDECL png_console_error_fn( png_structp png_ptr, png_const_charp error_msg ) {
- char *f = png_get_error_ptr( png_ptr );
+static void my_png_error_fn( png_structp png_ptr, png_const_charp error_msg ) {
+ my_png_error *err = png_get_error_ptr( png_ptr );
- Com_EPrintf( "LoadPNG: %s: %s\n", f, error_msg );
+ if( err->error == Q_ERR_LIBRARY_ERROR ) {
+ Com_EPrintf( "libpng: %s: %s\n", err->filename, error_msg );
+ }
longjmp( png_jmpbuf( png_ptr ), -1 );
}
-static void QDECL png_console_warning_fn( png_structp png_ptr, png_const_charp warning_msg ) {
- char *f = png_get_error_ptr( png_ptr );
+static void my_png_warning_fn( png_structp png_ptr, png_const_charp warning_msg ) {
+ my_png_error *err = png_get_error_ptr( png_ptr );
- Com_WPrintf( "LoadPNG: %s: %s\n", f, warning_msg );
+ Com_WPrintf( "libpng: %s: %s\n", err->filename, warning_msg );
}
/*
=================
-LoadPNG
+IMG_LoadPNG
=================
*/
-void IMG_LoadPNG( const char *filename, byte **pic, int *width, int *height ) {
+qerror_t IMG_LoadPNG( const char *filename, byte **pic, int *width, int *height ) {
byte *rawdata;
size_t rawlength;
- byte *pixels;
+ byte *volatile pixels;
png_bytep row_pointers[MAX_TEXTURE_SIZE];
png_uint_32 w, h, rowbytes, row;
int bitdepth, colortype;
png_structp png_ptr;
png_infop info_ptr;
- struct pngReadStruct r;
+ my_png_io my_io;
+ my_png_error my_err;
+ qerror_t ret;
if( !filename || !pic ) {
Com_Error( ERR_FATAL, "LoadPNG: NULL" );
@@ -951,47 +960,45 @@ void IMG_LoadPNG( const char *filename, byte **pic, int *width, int *height ) {
rawlength = FS_LoadFile( filename, ( void ** )&rawdata );
if( !rawdata ) {
- return;
+ return rawlength;
}
+ ret = Q_ERR_LIBRARY_ERROR;
+
+ my_err.filename = filename;
+ my_err.error = Q_ERR_LIBRARY_ERROR;
+
png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
- ( png_voidp )filename, png_console_error_fn, png_console_warning_fn );
+ ( png_voidp )&my_err, my_png_error_fn, my_png_warning_fn );
if( !png_ptr ) {
- goto fail;
+ goto fail1;
}
info_ptr = png_create_info_struct( png_ptr );
if( !info_ptr ) {
- png_destroy_read_struct( &png_ptr, NULL, NULL );
- goto fail;
+ goto fail2;
}
if( setjmp( png_jmpbuf( png_ptr ) ) ) {
- png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
- if( pixels ) {
- IMG_FreePixels( pixels );
- }
- goto fail;
+ IMG_FreePixels( pixels );
+ ret = my_err.error;
+ goto fail2;
}
- r.data = rawdata;
- r.maxp = rawdata + rawlength;
- png_set_read_fn( png_ptr, ( png_voidp )&r, png_vfs_read_fn );
+ my_io.next_in = rawdata;
+ my_io.avail_in = rawlength;
+ png_set_read_fn( png_ptr, ( png_voidp )&my_io, my_png_read_fn );
png_read_info( png_ptr, info_ptr );
- if( !png_get_IHDR( png_ptr, info_ptr, &w, &h, &bitdepth, &colortype,
- NULL, NULL, NULL ) )
- {
- png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
- goto fail;
+ if( !png_get_IHDR( png_ptr, info_ptr, &w, &h, &bitdepth, &colortype, NULL, NULL, NULL ) ) {
+ goto fail2;
}
if( w > MAX_TEXTURE_SIZE || h > MAX_TEXTURE_SIZE ) {
- Com_EPrintf( "LoadPNG: %s: oversize image dimensions: %lux%lu\n",
- filename, w, h );
- png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
- goto fail;
+ Com_DPrintf( "%s: %s: invalid image dimensions\n", __func__, filename );
+ ret = Q_ERR_INVALID_FORMAT;
+ goto fail2;
}
switch( colortype ) {
@@ -1033,67 +1040,74 @@ void IMG_LoadPNG( const char *filename, byte **pic, int *width, int *height ) {
png_read_end( png_ptr, info_ptr );
- png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
-
*pic = pixels;
*width = w;
*height = h;
+ ret = Q_ERR_SUCCESS;
-fail:
+fail2:
+ png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
+fail1:
FS_FreeFile( rawdata );
+ return ret;
}
-static void QDECL png_vfs_write_fn( png_structp png_ptr, png_bytep buf, png_size_t size ) {
- fileHandle_t *f = png_get_io_ptr( png_ptr );
- FS_Write( buf, size, *f );
-}
+static void my_png_write_fn( png_structp png_ptr, png_bytep buf, png_size_t size ) {
+ qhandle_t *f = png_get_io_ptr( png_ptr );
+ ssize_t ret = FS_Write( buf, size, *f );
-static void QDECL png_vfs_flush_fn( png_structp png_ptr ) {
- //fileHandle_t *f = png_get_io_ptr( png_ptr );
- //FS_Flush( *f );
+ if( ret != size ) {
+ my_png_error *err = png_get_error_ptr( png_ptr );
+ err->error = ret < 0 ? ret : Q_ERR_FAILURE;
+ png_error( png_ptr, "write error" );
+ }
}
-qboolean IMG_WritePNG( const char *filename, const byte *rgb, int width, int height, int compression ) {
+static void my_png_flush_fn( png_structp png_ptr ) { }
+
+/*
+=================
+IMG_SavePNG
+=================
+*/
+qerror_t IMG_SavePNG( qhandle_t f, const char *filename, const byte *rgb, int width, int height, int compression ) {
png_structp png_ptr;
png_infop info_ptr;
- fileHandle_t f;
- qboolean ret = qfalse;
- png_bytepp row_pointers = NULL;
- int row_stride;
- int i;
+ volatile png_bytepp row_pointers;
+ int i, row_stride;
+ my_png_error my_err;
+ qerror_t ret;
- FS_FOpenFile( filename, &f, FS_MODE_WRITE );
- if( !f ) {
- Com_DPrintf( "WritePNG: %s: couldn't create file\n", filename );
- return qfalse;
- }
+ row_pointers = NULL;
+ ret = Q_ERR_LIBRARY_ERROR;
+
+ my_err.filename = filename;
+ my_err.error = Q_ERR_LIBRARY_ERROR;
png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING,
- ( png_voidp )filename, png_console_error_fn, png_console_warning_fn );
+ ( png_voidp )&my_err, my_png_error_fn, my_png_warning_fn );
if( !png_ptr ) {
- goto fail;
+ goto fail1;
}
info_ptr = png_create_info_struct( png_ptr );
if( !info_ptr ) {
- png_destroy_write_struct( &png_ptr, NULL );
- goto fail;
+ goto fail2;
}
if( setjmp( png_jmpbuf( png_ptr ) ) ) {
- png_destroy_write_struct( &png_ptr, &info_ptr );
- goto fail;
+ ret = my_err.error;
+ goto fail3;
}
png_set_write_fn( png_ptr, ( png_voidp )&f,
- png_vfs_write_fn, png_vfs_flush_fn );
+ my_png_write_fn, my_png_flush_fn );
png_set_IHDR( png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
- PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
- PNG_FILTER_TYPE_DEFAULT );
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT );
- clamp( compression, Z_NO_COMPRESSION, Z_BEST_COMPRESSION );
- png_set_compression_level( png_ptr, compression );
+ png_set_compression_level( png_ptr,
+ clamp( compression, Z_NO_COMPRESSION, Z_BEST_COMPRESSION ) );
row_pointers = FS_AllocTempMem( sizeof( png_bytep ) * height );
row_stride = width * 3;
@@ -1105,19 +1119,17 @@ qboolean IMG_WritePNG( const char *filename, const byte *rgb, int width, int hei
png_write_png( png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL );
- png_destroy_write_struct( &png_ptr, &info_ptr );
-
- ret = qtrue;
+ ret = Q_ERR_SUCCESS;
-fail:
- if( row_pointers ) {
- FS_FreeFile( row_pointers );
- }
- FS_FCloseFile( f );
+fail3:
+ FS_FreeFile( row_pointers );
+fail2:
+ png_destroy_write_struct( &png_ptr, &info_ptr );
+fail1:
return ret;
}
-#endif /* USE_PNG */
+#endif // USE_PNG
/*
=========================================================
@@ -1129,7 +1141,6 @@ IMAGE MANAGER
#define RIMAGES_HASH 256
-
image_t r_images[MAX_RIMAGES];
list_t r_imageHash[RIMAGES_HASH];
int r_numImages;
@@ -1147,6 +1158,7 @@ IMG_List_f
===============
*/
static void IMG_List_f( void ) {
+ static const char types[8] = "MSWPYC?";
int i;
image_t *image;
int texels, count;
@@ -1157,36 +1169,15 @@ static void IMG_List_f( void ) {
for( i = 0, image = r_images; i < r_numImages; i++, image++ ) {
if( !image->registration_sequence )
continue;
- texels += image->upload_width * image->upload_height;
- switch( image->type ) {
- case it_skin:
- Com_Printf( "M" );
- break;
- case it_sprite:
- Com_Printf( "S" );
- break;
- case it_wall:
- Com_Printf( "W" );
- break;
- case it_pic:
- Com_Printf( "P" );
- break;
- case it_sky:
- Com_Printf( "Y" );
- break;
- case it_charset:
- Com_Printf( "C" );
- break;
- default:
- Com_Printf( " " );
- break;
- }
- Com_Printf( " %4i %4i %s: %s\n",
+ Com_Printf( "%c %4i %4i %s: %s\n",
+ types[image->type],
image->upload_width,
image->upload_height,
( image->flags & if_paletted ) ? "PAL" : "RGB",
image->name );
+
+ texels += image->upload_width * image->upload_height;
count++;
}
Com_Printf( "Total images: %d (out of %d slots)\n", count, r_numImages );
@@ -1267,48 +1258,52 @@ IMG_Find
Finds or loads the given image, adding it to the hash table.
===============
*/
-image_t *IMG_Find( const char *name, imagetype_t type ) {
+static qerror_t _IMG_Find( const char *name, imagetype_t type, image_t **image_p ) {
image_t *image;
byte *pic;
int width, height;
char buffer[MAX_QPATH];
char *ext;
- size_t length;
+ size_t len;
unsigned hash, extHash;
imageflags_t flags;
#if USE_PNG || USE_JPG || USE_TGA
char *s;
#endif
+ qerror_t err;
+
+ *image_p = NULL;
if( !name ) {
Com_Error( ERR_FATAL, "%s: NULL", __func__ );
}
- length = strlen( name );
- if( length >= MAX_QPATH ) {
+ len = strlen( name );
+ if( len >= MAX_QPATH ) {
Com_Error( ERR_FATAL, "%s: oversize name", __func__ );
}
- if( length <= 4 ) {
- return NULL; // must have at least 1 char of base name
+ if( len <= 4 ) {
+ return Q_ERR_INVALID_PATH; // must have at least 1 char of base name
}
- length -= 4;
- if( name[length] != '.' ) {
- return NULL;
+ len -= 4;
+ if( name[len] != '.' ) {
+ return Q_ERR_INVALID_PATH;
}
strcpy( buffer, name );
- buffer[length] = 0;
+ buffer[len] = 0;
hash = Com_HashPath( buffer, RIMAGES_HASH );
- if( ( image = IMG_Lookup( buffer, type, hash, length ) ) != NULL ) {
+ if( ( image = IMG_Lookup( buffer, type, hash, len ) ) != NULL ) {
image->registration_sequence = registration_sequence;
- return image;
+ *image_p = image;
+ return Q_ERR_SUCCESS;
}
- ext = buffer + length;
+ ext = buffer + len;
Q_strlwr( ext + 1 );
extHash = MakeRawLong( '.', ext[1], ext[2], ext[3] );
@@ -1325,22 +1320,24 @@ image_t *IMG_Find( const char *name, imagetype_t type ) {
#if USE_PNG
case 'p': // try *.png
strcpy( ext, ".png" );
- IMG_LoadPNG( buffer, &pic, &width, &height );
+ err = IMG_LoadPNG( buffer, &pic, &width, &height );
break;
#endif
#if USE_JPG
case 'j': // try *.jpg
strcpy( ext, ".jpg" );
- IMG_LoadJPG( buffer, &pic, &width, &height );
+ err = IMG_LoadJPG( buffer, &pic, &width, &height );
break;
#endif
#if USE_TGA
case 't': // try *.tga
strcpy( ext, ".tga" );
- IMG_LoadTGA( buffer, &pic, &width, &height );
+ err = IMG_LoadTGA( buffer, &pic, &width, &height );
break;
- }
#endif
+ default:
+ continue;
+ }
if( pic ) {
// replacing 8 bit texture with 32 bit texture
if( extHash == EXTENSION_WAL ) {
@@ -1350,28 +1347,27 @@ image_t *IMG_Find( const char *name, imagetype_t type ) {
}
goto create;
}
+ if( err != Q_ERR_NOENT ) {
+ return err;
+ }
}
- switch( extHash ) {
- case EXTENSION_PNG:
- case EXTENSION_TGA:
- case EXTENSION_JPG:
- case EXTENSION_PCX:
+ if( extHash == EXTENSION_WAL ) {
+ strcpy( ext, ".wal" );
+ if( ( image = IMG_LoadWAL( buffer ) ) != NULL ) {
+ goto append;
+ }
+ err = Q_ERR_NOENT;
+ } else {
strcpy( ext, ".pcx" );
- IMG_LoadPCX( buffer, &pic, NULL, &width, &height );
+ err = IMG_LoadPCX( buffer, &pic, NULL, &width, &height );
if( pic ) {
flags |= if_paletted;
goto create;
}
- return NULL;
- case EXTENSION_WAL:
- strcpy( ext, ".wal" );
- if( ( image = IMG_LoadWAL( buffer ) ) != NULL ) {
- goto append;
- }
}
- return NULL;
+ return err;
}
#endif
@@ -1380,10 +1376,13 @@ image_t *IMG_Find( const char *name, imagetype_t type ) {
#if USE_PNG
// try *.png
strcpy( ext, ".png" );
- IMG_LoadPNG( buffer, &pic, &width, &height );
+ err = IMG_LoadPNG( buffer, &pic, &width, &height );
if( pic ) {
goto create;
}
+ if( err != Q_ERR_NOENT ) {
+ return err;
+ }
#endif
#if USE_JPG || USE_TGA
for( s = r_texture_formats->string; *s; s++ ) {
@@ -1391,37 +1390,45 @@ image_t *IMG_Find( const char *name, imagetype_t type ) {
#if USE_JPG
case 'j': // try *.jpg
strcpy( ext, ".jpg" );
- IMG_LoadJPG( buffer, &pic, &width, &height );
+ err = IMG_LoadJPG( buffer, &pic, &width, &height );
break;
#endif
#if USE_TGA
case 't': // try *.tga
strcpy( ext, ".tga" );
- IMG_LoadTGA( buffer, &pic, &width, &height );
+ err = IMG_LoadTGA( buffer, &pic, &width, &height );
break;
- }
#endif
+ default:
+ continue;
+ }
if( pic ) {
goto create;
}
+ if( err != Q_ERR_NOENT ) {
+ return err;
+ }
}
#endif
// try *.pcx
strcpy( ext, ".pcx" );
- IMG_LoadPCX( buffer, &pic, NULL, &width, &height );
+ err = IMG_LoadPCX( buffer, &pic, NULL, &width, &height );
if( pic ) {
flags |= if_paletted;
goto create;
}
- return NULL;
+ return err;
case EXTENSION_TGA:
#if USE_TGA
strcpy( ext, ".tga" );
- IMG_LoadTGA( buffer, &pic, &width, &height );
+ err = IMG_LoadTGA( buffer, &pic, &width, &height );
if( pic ) {
goto create;
}
+ if( err != Q_ERR_NOENT ) {
+ return err;
+ }
#endif
#if USE_PNG || USE_JPG
@@ -1430,37 +1437,45 @@ image_t *IMG_Find( const char *name, imagetype_t type ) {
#if USE_PNG
case 'p': // try *.png
strcpy( ext, ".png" );
- IMG_LoadPNG( buffer, &pic, &width, &height );
+ err = IMG_LoadPNG( buffer, &pic, &width, &height );
break;
#endif
#if USE_JPG
case 'j': // try *.jpg
strcpy( ext, ".jpg" );
- IMG_LoadJPG( buffer, &pic, &width, &height );
+ err = IMG_LoadJPG( buffer, &pic, &width, &height );
break;
#endif
+ default:
+ continue;
}
if( pic ) {
goto create;
}
+ if( err != Q_ERR_NOENT ) {
+ return err;
+ }
}
#endif
// try *.pcx
strcpy( ext, ".pcx" );
- IMG_LoadPCX( buffer, &pic, NULL, &width, &height );
+ err = IMG_LoadPCX( buffer, &pic, NULL, &width, &height );
if( pic ) {
flags |= if_paletted;
goto create;
}
- return NULL;
+ return err;
case EXTENSION_JPG:
#if USE_JPG
strcpy( ext, ".jpg" );
- IMG_LoadJPG( buffer, &pic, &width, &height );
+ err = IMG_LoadJPG( buffer, &pic, &width, &height );
if( pic ) {
goto create;
}
+ if( err != Q_ERR_NOENT ) {
+ return err;
+ }
#endif
#if USE_PNG || USE_TGA
@@ -1469,39 +1484,47 @@ image_t *IMG_Find( const char *name, imagetype_t type ) {
#if USE_PNG
case 'p': // try *.png
strcpy( ext, ".png" );
- IMG_LoadPNG( buffer, &pic, &width, &height );
+ err = IMG_LoadPNG( buffer, &pic, &width, &height );
break;
#endif
#if USE_TGA
case 't': // try *.tga
strcpy( ext, ".tga" );
- IMG_LoadTGA( buffer, &pic, &width, &height );
+ err = IMG_LoadTGA( buffer, &pic, &width, &height );
break;
- }
#endif
+ default:
+ continue;
+ }
if( pic ) {
goto create;
}
+ if( err != Q_ERR_NOENT ) {
+ return err;
+ }
}
#endif
// try *.pcx
strcpy( ext, ".pcx" );
- IMG_LoadPCX( buffer, &pic, NULL, &width, &height );
+ err = IMG_LoadPCX( buffer, &pic, NULL, &width, &height );
if( pic ) {
flags |= if_paletted;
goto create;
}
- return NULL;
+ return err;
case EXTENSION_PCX:
strcpy( ext, ".pcx" );
- IMG_LoadPCX( buffer, &pic, NULL, &width, &height );
+ err = IMG_LoadPCX( buffer, &pic, NULL, &width, &height );
if( pic ) {
flags |= if_paletted;
goto create;
}
+ if( err != Q_ERR_NOENT ) {
+ return err;
+ }
#if USE_PNG || USE_JPG || USE_TGA
for( s = r_texture_formats->string; *s; s++ ) {
@@ -1509,28 +1532,33 @@ image_t *IMG_Find( const char *name, imagetype_t type ) {
#if USE_PNG
case 'p': // try *.png
strcpy( ext, ".png" );
- IMG_LoadPNG( buffer, &pic, &width, &height );
+ err = IMG_LoadPNG( buffer, &pic, &width, &height );
break;
#endif
#if USE_JPG
case 'j': // try *.jpg
strcpy( ext, ".jpg" );
- IMG_LoadJPG( buffer, &pic, &width, &height );
+ err = IMG_LoadJPG( buffer, &pic, &width, &height );
break;
#endif
#if USE_TGA
case 't': // try *.tga
strcpy( ext, ".tga" );
- IMG_LoadTGA( buffer, &pic, &width, &height );
+ err = IMG_LoadTGA( buffer, &pic, &width, &height );
break;
- }
#endif
+ default:
+ continue;
+ }
if( pic ) {
goto create;
}
+ if( err != Q_ERR_NOENT ) {
+ return err;
+ }
}
#endif
- return NULL;
+ return Q_ERR_NOENT;
case EXTENSION_WAL:
strcpy( ext, ".wal" );
@@ -1545,38 +1573,61 @@ image_t *IMG_Find( const char *name, imagetype_t type ) {
#if USE_PNG
case 'p': // try *.png
strcpy( ext, ".png" );
- IMG_LoadPNG( buffer, &pic, &width, &height );
+ err = IMG_LoadPNG( buffer, &pic, &width, &height );
break;
#endif
#if USE_JPG
case 'j': // try *.jpg
strcpy( ext, ".jpg" );
- IMG_LoadJPG( buffer, &pic, &width, &height );
+ err = IMG_LoadJPG( buffer, &pic, &width, &height );
break;
#endif
#if USE_TGA
case 't': // try *.tga
strcpy( ext, ".tga" );
- IMG_LoadTGA( buffer, &pic, &width, &height );
+ err = IMG_LoadTGA( buffer, &pic, &width, &height );
break;
- }
#endif
+ default:
+ continue;
+ }
if( pic ) {
goto create;
}
+ if( err != Q_ERR_NOENT ) {
+ return err;
+ }
}
#endif
- return NULL;
+ return Q_ERR_NOENT;
default:
- return NULL;
+ return Q_ERR_INVALID_PATH;
}
create:
image = IMG_Create( buffer, pic, width, height, type, flags );
append:
List_Append( &r_imageHash[hash], &image->entry );
- return image;
+ *image_p = image;
+ return Q_ERR_SUCCESS;
+}
+
+image_t *IMG_Find( const char *name, imagetype_t type ) {
+ image_t *image;
+ qerror_t ret;
+
+ ret = _IMG_Find( name, type, &image );
+ if( image ) {
+ return image;
+ }
+
+ // don't spam about missing images
+ if( ret != Q_ERR_NOENT ) {
+ Com_EPrintf( "Couldn't load %s: %s\n", name, Q_ErrorString( ret ) );
+ }
+
+ return NULL;
}
/*
@@ -1611,62 +1662,81 @@ qhandle_t R_RegisterSkin( const char *name ) {
return ( image - r_images );
}
-/*
-================
-R_RegisterPic
-================
-*/
-qhandle_t R_RegisterPic( const char *name ) {
+static qerror_t _register_image( const char *name, imagetype_t type, qhandle_t *handle ) {
image_t *image;
char fullname[MAX_QPATH];
+ size_t len;
+ qerror_t ret;
+
+ *handle = 0;
if( !r_numImages ) {
- return 0;
+ return Q_ERR_AGAIN;
}
- if( name[0] == '*' ) {
- image = IMG_Find( name + 1, it_tmp );
- } else if( name[0] == '/' || name[0] == '\\' ) {
- image = IMG_Find( name + 1, it_pic );
+ if( name[0] == '/' || name[0] == '\\' ) {
+ ret = _IMG_Find( name + 1, type, &image );
} else {
- Q_concat( fullname, sizeof( fullname ), "pics/", name, NULL );
- COM_DefaultExtension( fullname, ".pcx", sizeof( fullname ) );
- image = IMG_Find( fullname, it_pic );
+ len = Q_concat( fullname, sizeof( fullname ), "pics/", name, NULL );
+ if( len >= sizeof( fullname ) ) {
+ return Q_ERR_NAMETOOLONG;
+ }
+ len = COM_DefaultExtension( fullname, ".pcx", sizeof( fullname ) );
+ if( len >= sizeof( fullname ) ) {
+ return Q_ERR_NAMETOOLONG;
+ }
+ ret = _IMG_Find( fullname, type, &image );
}
if( !image ) {
- return 0;
+ return ret;
}
- return ( image - r_images );
+ *handle = ( image - r_images );
+ return Q_ERR_SUCCESS;
+}
+
+static qhandle_t register_image( const char *name, imagetype_t type ) {
+ qhandle_t handle;
+ qerror_t ret;
+
+ ret = _register_image( name, type, &handle );
+ if( handle ) {
+ return handle;
+ }
+
+ // don't spam about missing images
+ if( ret != Q_ERR_NOENT ) {
+ Com_EPrintf( "Couldn't load %s: %s\n", name, Q_ErrorString( ret ) );
+ }
+
+ return 0;
}
/*
================
-R_RegisterFont
+R_RegisterPic
================
*/
-qhandle_t R_RegisterFont( const char *name ) {
- image_t *image;
- char fullname[MAX_QPATH];
-
- if( !r_numImages ) {
- return 0;
- }
+qhandle_t R_RegisterPic( const char *name ) {
+ return register_image( name, it_pic );
+}
- if( name[0] == '/' || name[0] == '\\' ) {
- image = IMG_Find( name + 1, it_charset );
- } else {
- Q_concat( fullname, sizeof( fullname ), "pics/", name, NULL );
- COM_DefaultExtension( fullname, ".pcx", sizeof( fullname ) );
- image = IMG_Find( fullname, it_charset );
- }
+qerror_t _R_RegisterPic( const char *name, qhandle_t *handle ) {
+ return _register_image( name, it_pic, handle );
+}
- if( !image ) {
- return 0;
- }
+/*
+================
+R_RegisterFont
+================
+*/
+qhandle_t R_RegisterFont( const char *name ) {
+ return register_image( name, it_charset );
+}
- return ( image - r_images );
+qerror_t _R_RegisterFont( const char *name, qhandle_t *handle ) {
+ return _register_image( name, it_charset, handle );
}
/*
@@ -1746,13 +1816,14 @@ the colormap for software renderer.
===============
*/
void IMG_GetPalette( byte **pic ) {
- int i;
+ int i, ret;
byte pal[768], *src;
int w, h;
// get the palette
- if( !IMG_LoadPCX( "pics/colormap.pcx", pic, pal, &w, &h ) ) {
- Com_Error( ERR_FATAL, "Couldn't load pics/colormap.pcx" );
+ ret = IMG_LoadPCX( "pics/colormap.pcx", pic, pal, &w, &h );
+ if( ret < 0 ) {
+ Com_Error( ERR_FATAL, "Couldn't load pics/colormap.pcx: %s", Q_ErrorString( ret ) );
}
for( i = 0, src = pal; i < 255; i++, src += 3 ) {
diff --git a/source/r_models.c b/source/r_models.c
index 9d9cd21..f862fd3 100644
--- a/source/r_models.c
+++ b/source/r_models.c
@@ -132,86 +132,72 @@ void MOD_FreeAll( void ) {
r_numModels = 0;
}
-qboolean MOD_ValidateMD2( model_t *model, dmd2header_t *header, size_t length ) {
+qerror_t MOD_ValidateMD2( model_t *model, dmd2header_t *header, size_t length ) {
size_t end;
// check ident and version
- if( header->ident != MD2_IDENT ) {
- Com_WPrintf( "%s is not an MD2 file\n", model->name );
- return qfalse;
- }
- if( header->version != MD2_VERSION ) {
- Com_WPrintf( "%s has bad version: %d instead of %d\n",
- model->name, header->version, MD2_VERSION );
- return qfalse;
- }
+ if( header->ident != MD2_IDENT )
+ return Q_ERR_UNKNOWN_FORMAT;
+ if( header->version != MD2_VERSION )
+ return Q_ERR_UNKNOWN_FORMAT;
// check triangles
- if( header->num_tris < 1 || header->num_tris > MD2_MAX_TRIANGLES ) {
- Com_WPrintf( "%s has bad number of triangles\n", model->name );
- return qfalse;
- }
+ if( header->num_tris < 1 )
+ return Q_ERR_TOO_FEW;
+ if( header->num_tris > MD2_MAX_TRIANGLES )
+ return Q_ERR_TOO_MANY;
+
end = header->ofs_tris + sizeof( dmd2triangle_t ) * header->num_tris;
- if( header->ofs_tris < sizeof( header ) || end < header->ofs_tris || end > length ) {
- Com_WPrintf( "%s has bad triangles offset\n", model->name );
- return qfalse;
- }
+ if( header->ofs_tris < sizeof( header ) || end < header->ofs_tris || end > length )
+ return Q_ERR_BAD_EXTENT;
// check st
- if( header->num_st < 3 || header->num_st > MD2_MAX_VERTS ) {
- Com_WPrintf( "%s has bad number of st\n", model->name );
- return qfalse;
- }
+ if( header->num_st < 3 )
+ return Q_ERR_TOO_FEW;
+ if( header->num_st > MD2_MAX_VERTS )
+ return Q_ERR_TOO_MANY;
+
end = header->ofs_st + sizeof( dmd2stvert_t ) * header->num_st;
- if( header->ofs_st < sizeof( header ) || end < header->ofs_st || end > length ) {
- Com_WPrintf( "%s has bad st offset\n", model->name );
- return qfalse;
- }
+ if( header->ofs_st < sizeof( header ) || end < header->ofs_st || end > length )
+ return Q_ERR_BAD_EXTENT;
// check xyz and frames
- if( header->num_xyz < 3 || header->num_xyz > MD2_MAX_VERTS ) {
- Com_WPrintf( "%s has bad number of xyz\n", model->name );
- return qfalse;
- }
- if( header->num_frames < 1 || header->num_frames > MD2_MAX_FRAMES ) {
- Com_WPrintf( "%s has bad number of frames\n", model->name );
- return qfalse;
- }
+ if( header->num_xyz < 3 )
+ return Q_ERR_TOO_FEW;
+ if( header->num_xyz > MD2_MAX_VERTS )
+ return Q_ERR_TOO_MANY;
+ if( header->num_frames < 1 )
+ return Q_ERR_TOO_FEW;
+ if( header->num_frames > MD2_MAX_FRAMES )
+ return Q_ERR_TOO_MANY;
+
end = sizeof( dmd2frame_t ) + ( header->num_xyz - 1 ) * sizeof( dmd2trivertx_t );
- if( header->framesize < end || header->framesize > MD2_MAX_FRAMESIZE ) {
- Com_WPrintf( "%s has bad frame size\n", model->name );
- return qfalse;
- }
- end = header->ofs_frames + header->framesize * header->num_frames;
- if( header->ofs_frames < sizeof( header ) || end < header->ofs_frames || end > length ) {
- Com_WPrintf( "%s has bad frames offset\n", model->name );
- return qfalse;
- }
+ if( header->framesize < end || header->framesize > MD2_MAX_FRAMESIZE )
+ return Q_ERR_BAD_EXTENT;
+
+ end = header->ofs_frames + ( size_t )header->framesize * header->num_frames;
+ if( header->ofs_frames < sizeof( header ) || end < header->ofs_frames || end > length )
+ return Q_ERR_BAD_EXTENT;
// check skins
- if( header->num_skins > MAX_ALIAS_SKINS ) {
- Com_WPrintf( "%s has bad number of skins\n", model->name );
- return qfalse;
- }
if( header->num_skins ) {
- end = header->ofs_skins + MD2_MAX_SKINNAME * header->num_skins;
- if( header->ofs_skins < sizeof( header ) || end < header->ofs_skins || end > length ) {
- Com_WPrintf( "%s has bad skins offset\n", model->name );
- return qfalse;
- }
- }
- if( header->skinwidth < 1 || header->skinwidth > MD2_MAX_SKINWIDTH ) {
- Com_WPrintf( "%s has bad skin width\n", model->name );
- return qfalse;
- }
- if( header->skinheight < 1 || header->skinheight > MD2_MAX_SKINHEIGHT ) {
- Com_WPrintf( "%s has bad skin height\n", model->name );
- return qfalse;
+ if( header->num_skins > MAX_ALIAS_SKINS )
+ return Q_ERR_TOO_MANY;
+
+ end = header->ofs_skins + ( size_t )MD2_MAX_SKINNAME * header->num_skins;
+ if( header->ofs_skins < sizeof( header ) || end < header->ofs_skins || end > length )
+ return Q_ERR_BAD_EXTENT;
}
- return qtrue;
+
+ if( header->skinwidth < 1 || header->skinwidth > MD2_MAX_SKINWIDTH )
+ return Q_ERR_INVALID_FORMAT;
+ if( header->skinheight < 1 || header->skinheight > MD2_MAX_SKINHEIGHT )
+ return Q_ERR_INVALID_FORMAT;
+
+ return Q_ERR_SUCCESS;
}
-static qboolean MOD_LoadSP2( model_t *model, const void *rawdata, size_t length ) {
+static qerror_t MOD_LoadSP2( model_t *model, const void *rawdata, size_t length ) {
dsp2header_t header;
dsp2frame_t *src_frame;
mspriteframe_t *dst_frame;
@@ -220,10 +206,8 @@ static qboolean MOD_LoadSP2( model_t *model, const void *rawdata, size_t length
image_t *image;
int i;
- if( length < sizeof( header ) ) {
- Com_EPrintf( "%s is too small\n", model->name );
- return qfalse;
- }
+ if( length < sizeof( header ) )
+ return Q_ERR_FILE_TOO_SMALL;
// byte swap the header
header = *( dsp2header_t * )rawdata;
@@ -231,24 +215,16 @@ static qboolean MOD_LoadSP2( model_t *model, const void *rawdata, size_t length
(( uint32_t * )&header)[i] = LittleLong( (( uint32_t * )&header)[i] );
}
- if( header.ident != SP2_IDENT ) {
- Com_EPrintf( "%s is not an SP2 file\n", model->name );
- return qfalse;
- }
- if( header.version != SP2_VERSION ) {
- Com_EPrintf( "%s has bad version: %d instead of %d\n",
- model->name, header.version, SP2_VERSION );
- return qfalse;
- }
- if( header.numframes < 1 || header.numframes > SP2_MAX_FRAMES ) {
- Com_EPrintf( "%s has bad number of frames: %d\n",
- model->name, header.numframes );
- return qfalse;
- }
- if( sizeof( dsp2header_t ) + sizeof( dsp2frame_t ) * header.numframes > length ) {
- Com_EPrintf( "%s has frames out of bounds\n", model->name );
- return qfalse;
- }
+ if( header.ident != SP2_IDENT )
+ return Q_ERR_UNKNOWN_FORMAT;
+ if( header.version != SP2_VERSION )
+ return Q_ERR_UNKNOWN_FORMAT;
+ if( header.numframes < 1 )
+ return Q_ERR_TOO_FEW;
+ if( header.numframes > SP2_MAX_FRAMES )
+ return Q_ERR_TOO_MANY;
+ if( sizeof( dsp2header_t ) + sizeof( dsp2frame_t ) * header.numframes > length )
+ return Q_ERR_BAD_EXTENT;
Hunk_Begin( &model->pool, 0x10000 );
@@ -292,17 +268,18 @@ static qboolean MOD_LoadSP2( model_t *model, const void *rawdata, size_t length
Hunk_End( &model->pool );
- return qtrue;
+ return Q_ERR_SUCCESS;
}
-
qhandle_t R_RegisterModel( const char *name ) {
int index;
size_t namelen, filelen;
model_t *model;
byte *rawdata;
uint32_t ident;
- qboolean success;
+ mod_load_t load;
+ const char *truename;
+ qerror_t ret;
if( name[0] == '*' ) {
// inline bsp model
@@ -318,12 +295,9 @@ qhandle_t R_RegisterModel( const char *name ) {
model = MOD_Find( name );
if( model ) {
MOD_Reference( model );
- goto finish;
+ goto success;
}
- filelen = 0;
- rawdata = NULL;
-
#if USE_MD3
if( r_override_models->integer ) {
char buffer[MAX_QPATH];
@@ -331,55 +305,79 @@ qhandle_t R_RegisterModel( const char *name ) {
if( namelen > 4 && !Q_stricmp( name + namelen - 4, ".md2" ) ) {
memcpy( buffer, name, namelen + 1 );
buffer[namelen - 1] = '3';
- filelen = FS_LoadFile( buffer, ( void ** )&rawdata );
+ truename = buffer;
+
+ filelen = FS_LoadFile( truename, ( void ** )&rawdata );
+ if( rawdata ) {
+ goto found;
+ }
+ if( filelen != Q_ERR_NOENT ) {
+ ret = filelen;
+ goto fail1;
+ }
}
}
- if( !rawdata )
#endif
- {
- filelen = FS_LoadFile( name, ( void ** )&rawdata );
- if( !rawdata ) {
- Com_DPrintf( "Couldn't load %s\n", name );
- return 0;
- }
+
+ truename = name;
+
+ filelen = FS_LoadFile( truename, ( void ** )&rawdata );
+ if( rawdata ) {
+ goto found;
}
+ if( filelen != Q_ERR_NOENT ) {
+ ret = filelen;
+ goto fail1;
+ }
+
+ // don't spam about missing models
+ return 0;
+found:
if( filelen < 4 ) {
- Com_WPrintf( "%s: %s: file too short\n", __func__, name );
- return 0;
+ ret = Q_ERR_FILE_TOO_SMALL;
+ goto fail2;
}
- model = MOD_Alloc( name );
-
+ // check ident
ident = LittleLong( *( uint32_t * )rawdata );
switch( ident ) {
case MD2_IDENT:
- success = MOD_LoadMD2( model, rawdata, filelen );
+ load = MOD_LoadMD2;
break;
#if USE_MD3
case MD3_IDENT:
- success = MOD_LoadMD3( model, rawdata, filelen );
+ load = MOD_LoadMD3;
break;
#endif
case SP2_IDENT:
- success = MOD_LoadSP2( model, rawdata, filelen );
+ load = MOD_LoadSP2;
break;
default:
- Com_WPrintf( "%s: %s: unknown ident: %x\n", __func__, name, ident );
- success = qfalse;
- break;
+ ret = Q_ERR_UNKNOWN_FORMAT;
+ goto fail2;
}
+ model = MOD_Alloc( name );
+
+ ret = load( model, rawdata, filelen );
+
FS_FreeFile( rawdata );
- if( !success ) {
+ if( ret ) {
memset( model, 0, sizeof( *model ) );
- return 0;
+ goto fail1;
}
-finish:
+success:
index = ( model - r_models ) + 1;
return index;
+
+fail2:
+ FS_FreeFile( rawdata );
+fail1:
+ Com_EPrintf( "Couldn't load %s: %s\n", truename, Q_ErrorString( ret ) );
+ return 0;
}
model_t *MOD_ForHandle( qhandle_t h ) {
diff --git a/source/r_models.h b/source/r_models.h
index 966fd8d..ef5b33b 100644
--- a/source/r_models.h
+++ b/source/r_models.h
@@ -73,12 +73,14 @@ void MOD_Shutdown( void );
model_t *MOD_ForHandle( qhandle_t h );
qhandle_t R_RegisterModel( const char *name );
-// these are implemented in [gl,sw]_models.c
struct dmd2header_s;
-qboolean MOD_ValidateMD2( model_t *model, struct dmd2header_s *header, size_t length );
-qboolean MOD_LoadMD2( model_t *model, const void *rawdata, size_t length );
+qerror_t MOD_ValidateMD2( model_t *model, struct dmd2header_s *header, size_t length );
+
+// these are implemented in [gl,sw]_models.c
+typedef qerror_t (*mod_load_t)( model_t *, const void *, size_t );
+qerror_t MOD_LoadMD2( model_t *model, const void *rawdata, size_t length );
#if USE_MD3
-qboolean MOD_LoadMD3( model_t *model, const void *rawdata, size_t length );
+qerror_t MOD_LoadMD3( model_t *model, const void *rawdata, size_t length );
#endif
void MOD_Reference( model_t *model );
diff --git a/source/r_shared.h b/source/r_shared.h
index ae0fea5..7d4336e 100644
--- a/source/r_shared.h
+++ b/source/r_shared.h
@@ -79,8 +79,7 @@ typedef enum {
it_wall,
it_pic,
it_sky,
- it_charset,
- it_tmp
+ it_charset
} imagetype_t;
#define EXTENSION_PNG MakeRawLong( '.', 'p', 'n', 'g' )
@@ -133,24 +132,31 @@ qhandle_t R_RegisterSkin( const char *name );
qhandle_t R_RegisterPic( const char *name );
qhandle_t R_RegisterFont( const char *name );
-qboolean IMG_LoadPCX( const char *filename, byte **pic, byte *palette,
+qerror_t IMG_LoadPCX( const char *filename, byte **pic, byte *palette,
int *width, int *height );
+qerror_t IMG_WritePCX( qhandle_t f, const char *filename, const byte *data, int width,
+ int height, int rowbytes, byte *palette );
+
+#if USE_TGA || USE_JPG || USE_PNG
+typedef qerror_t (img_load_t)( const char *, byte **, int *, int * );
+typedef qerror_t (img_save_t)( qhandle_t, const char *, const byte *, int, int, int );
+#endif
#if USE_TGA
-void IMG_LoadTGA( const char *filename, byte **pic, int *width, int *height );
-qboolean IMG_WriteTGA( const char *filename, const byte *rgb,
- int width, int height );
+qerror_t IMG_LoadTGA( const char *filename, byte **pic, int *width, int *height );
+qerror_t IMG_SaveTGA( qhandle_t f, const char *filename, const byte *bgr,
+ int width, int height, int unused );
#endif
#if USE_JPG
-void IMG_LoadJPG( const char *filename, byte **pic, int *width, int *height );
-qboolean IMG_WriteJPG( const char *filename, const byte *rgb,
+qerror_t IMG_LoadJPG( const char *filename, byte **pic, int *width, int *height );
+qerror_t IMG_SaveJPG( qhandle_t f, const char *filename, const byte *rgb,
int width, int height, int quality );
#endif
#if USE_PNG
-void IMG_LoadPNG( const char *filename, byte **pic, int *width, int *height );
-qboolean IMG_WritePNG( const char *filename, const byte *rgb,
+qerror_t IMG_LoadPNG( const char *filename, byte **pic, int *width, int *height );
+qerror_t IMG_SavePNG( qhandle_t f, const char *filename, const byte *rgb,
int width, int height, int compression );
#endif
diff --git a/source/ref_public.h b/source/ref_public.h
index 6e92ba4..fa32bd4 100644
--- a/source/ref_public.h
+++ b/source/ref_public.h
@@ -201,6 +201,8 @@ qhandle_t R_RegisterModel( const char *name );
qhandle_t R_RegisterSkin( const char *name );
qhandle_t R_RegisterPic( const char *name );
qhandle_t R_RegisterFont( const char *name );
+qerror_t _R_RegisterPic( const char *name, qhandle_t *handle );
+qerror_t _R_RegisterFont( const char *name, qhandle_t *handle );
void R_SetSky( const char *name, float rotate, vec3_t axis );
void R_EndRegistration( void );
diff --git a/source/snd_mem.c b/source/snd_mem.c
index c347726..6f89469 100644
--- a/source/snd_mem.c
+++ b/source/snd_mem.c
@@ -164,8 +164,9 @@ static void FindNextChunk( uint32_t search ) {
data_p += length;
}
- Com_WPrintf( "%s: too many iterations for chunk %#x in %s\n",
+ Com_DPrintf( "%s: too many iterations for chunk %#x in %s\n",
__func__, search, s_info.name );
+ data_p = NULL;
}
static void FindChunk( uint32_t search ) {
@@ -181,11 +182,6 @@ static void FindChunk( uint32_t search ) {
#define TAG_MARK MakeRawLong( 'M', 'A', 'R', 'K' )
#define TAG_data MakeRawLong( 'd', 'a', 't', 'a' )
-/*
-============
-GetWavinfo
-============
-*/
static qboolean GetWavinfo( void ) {
int format;
int samples, width;
@@ -224,7 +220,7 @@ static qboolean GetWavinfo( void ) {
}
s_info.rate = GetLittleLong();
- if( s_info.rate <= 0 ) {
+ if( s_info.rate < 8000 || s_info.rate > 48000 ) {
Com_DPrintf( "%s has bad rate\n", s_info.name );
return qfalse;
}
@@ -249,15 +245,23 @@ static qboolean GetWavinfo( void ) {
if( data_p ) {
data_p += 24;
s_info.loopstart = GetLittleLong();
+ if( s_info.loopstart < 0 || s_info.loopstart > INT_MAX ) {
+ Com_DPrintf( "%s has bad loop start\n", s_info.name );
+ return qfalse;
+ }
FindNextChunk( TAG_LIST );
if( data_p ) {
data_p += 20;
chunk = GetLittleLong();
- if ( chunk == TAG_MARK ) {
+ if( chunk == TAG_MARK ) {
// this is not a proper parse, but it works with cooledit...
data_p += 16;
samples = GetLittleLong(); // samples in loop
+ if( samples < 0 || samples > INT_MAX - s_info.loopstart ) {
+ Com_DPrintf( "%s has bad loop length\n", s_info.name );
+ return qfalse;
+ }
s_info.samples = s_info.loopstart + samples;
}
}
@@ -298,11 +302,12 @@ S_LoadSound
==============
*/
sfxcache_t *S_LoadSound (sfx_t *s) {
- char namebuffer[MAX_QPATH];
- byte *data;
+ char namebuffer[MAX_QPATH];
+ byte *data;
sfxcache_t *sc;
- size_t size;
- char *name;
+ size_t len;
+ char *name;
+ qerror_t ret;
if (s->name[0] == '*')
return NULL;
@@ -319,34 +324,47 @@ sfxcache_t *S_LoadSound (sfx_t *s) {
name = s->name;
if (name[0] == '#')
- Q_strlcpy( namebuffer, name + 1, sizeof( namebuffer ) );
+ len = Q_strlcpy( namebuffer, name + 1, sizeof( namebuffer ) );
else
- Q_concat( namebuffer, sizeof( namebuffer ), "sound/", name, NULL );
+ len = Q_concat( namebuffer, sizeof( namebuffer ), "sound/", name, NULL );
+ if( len >= sizeof( namebuffer ) ) {
+ ret = Q_ERR_NAMETOOLONG;
+ goto fail1;
+ }
- size = FS_LoadFile (namebuffer, (void **)&data);
+ len = FS_LoadFile (namebuffer, (void **)&data);
if (!data) {
- Com_DPrintf ("Couldn't load %s\n", namebuffer);
- return NULL;
+ ret = len;
+ goto fail1;
}
memset( &s_info, 0, sizeof( s_info ) );
s_info.name = namebuffer;
iff_data = data;
- iff_end = data + size;
- if( GetWavinfo() ) {
+ iff_end = data + len;
+ if( !GetWavinfo() ) {
+ ret = Q_ERR_INVALID_FORMAT;
+ goto fail2;
+ }
+
#if USE_OPENAL
- if( s_started == SS_OAL )
- sc = AL_UploadSfx( s );
- else
+ if( s_started == SS_OAL )
+ sc = AL_UploadSfx( s );
+ else
#endif
#if USE_SNDDMA
- sc = ResampleSfx( s )
+ sc = ResampleSfx( s )
#endif
- ;
- }
+ ;
+ ret = Q_ERR_SUCCESS;
+fail2:
FS_FreeFile( data );
+fail1:
+ if( ret && ret != Q_ERR_NOENT ) {
+ Com_EPrintf( "Couldn't load %s: %s\n", namebuffer, Q_ErrorString( ret ) );
+ }
return sc;
}
diff --git a/source/snd_oss.c b/source/snd_oss.c
index 2e32146..6260530 100644
--- a/source/snd_oss.c
+++ b/source/snd_oss.c
@@ -31,7 +31,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <sys/soundcard.h>
#endif
#include <stdio.h>
-#include <errno.h>
#include "com_local.h"
#include "snd_local.h"
diff --git a/source/sv_ac.c b/source/sv_ac.c
index 2795c46..e481c9a 100644
--- a/source/sv_ac.c
+++ b/source/sv_ac.c
@@ -392,9 +392,14 @@ static void AC_ParseToken( char *data, int linenum, const char *path ) {
static qboolean AC_ParseFile( const char *path, ac_parse_t parse, int depth ) {
char *raw, *data, *p;
int linenum = 1;
+ qerror_t ret;
- FS_LoadFile( path, ( void ** )&raw );
+ ret = FS_LoadFile( path, ( void ** )&raw );
if( !raw ) {
+ if( ret != Q_ERR_NOENT || depth ) {
+ Com_WPrintf( "ANTICHEAT: Could not %s %s: %s\n",
+ depth ? "include" : "load", path, Q_ErrorString( ret ) );
+ }
return qfalse;
}
@@ -417,8 +422,8 @@ static qboolean AC_ParseFile( const char *path, ac_parse_t parse, int depth ) {
if( !strncmp( data + 1, "include ", 8 ) ) {
if( depth == AC_MAX_INCLUDES ) {
Com_WPrintf( "ANTICHEAT: Includes too deeply nested.\n" );
- } else if( !AC_ParseFile( data + 9, parse, depth + 1 ) ) {
- Com_WPrintf( "ANTICHEAT: Could not include %s\n", data + 9 );
+ } else {
+ AC_ParseFile( data + 9, parse, depth + 1 );
}
} else {
Com_WPrintf( "ANTICHEAT: Unknown directive %s on line %d in %s\n", data + 1, linenum, path );
diff --git a/source/sv_ccmds.c b/source/sv_ccmds.c
index 5692e67..0a8e7e2 100644
--- a/source/sv_ccmds.c
+++ b/source/sv_ccmds.c
@@ -320,9 +320,9 @@ static void SV_Map_c( genctx_t *ctx, int argnum ) {
static void SV_DumpEnts_f( void ) {
bsp_t *c = sv.cm.cache;
- fileHandle_t f;
char buffer[MAX_OSPATH];
size_t len;
+ qerror_t ret;
if( !c || !c->entitystring ) {
Com_Printf( "No map loaded.\n" );
@@ -341,15 +341,12 @@ static void SV_DumpEnts_f( void ) {
return;
}
- FS_FOpenFile( buffer, &f, FS_MODE_WRITE );
- if( !f ) {
- Com_EPrintf( "Couldn't open %s for writing\n", buffer );
+ ret = FS_WriteFile( buffer, c->entitystring, c->numentitychars );
+ if( ret < 0 ) {
+ Com_EPrintf( "Couldn't write %s: %s\n", buffer, Q_ErrorString( ret ) );
return;
}
- FS_Write( c->entitystring, c->numentitychars, f );
- FS_FCloseFile( f );
-
Com_Printf( "Dumped entity string to %s\n", buffer );
}
diff --git a/source/sv_init.c b/source/sv_init.c
index e439c02..59e58ee 100644
--- a/source/sv_init.c
+++ b/source/sv_init.c
@@ -308,6 +308,7 @@ void SV_Map (const char *levelstring, qboolean restart) {
char expanded[MAX_QPATH];
char *ch;
cm_t cm;
+ qerror_t ret;
// skip the end-of-unit flag if necessary
if( *levelstring == '*' ) {
@@ -336,8 +337,9 @@ void SV_Map (const char *levelstring, qboolean restart) {
}
Q_concat( expanded, sizeof( expanded ), "maps/", level, ".bsp", NULL );
- if( !CM_LoadMap( &cm, expanded ) ) {
- Com_Printf( "Couldn't load %s: %s\n", expanded, BSP_GetError() );
+ ret = CM_LoadMap( &cm, expanded );
+ if( ret < 0 ) {
+ Com_Printf( "Couldn't load %s: %s\n", expanded, Q_ErrorString( ret ) );
return;
}
diff --git a/source/sv_mvd.c b/source/sv_mvd.c
index 16d959e..a425938 100644
--- a/source/sv_mvd.c
+++ b/source/sv_mvd.c
@@ -73,7 +73,7 @@ typedef struct {
entity_state_t *entities; // [MAX_EDICTS]
// local recorder
- fileHandle_t recording;
+ qhandle_t recording;
int numlevels; // stop after that many levels
int numframes; // stop after that many frames
@@ -119,7 +119,7 @@ static void flush_stream( gtv_client_t *client, int flush );
static void rec_stop( void );
static qboolean rec_allowed( void );
-static void rec_start( fileHandle_t demofile );
+static void rec_start( qhandle_t demofile );
static void rec_write( void );
@@ -163,8 +163,7 @@ static void dummy_forward_f( void ) {
static void dummy_record_f( void ) {
char buffer[MAX_OSPATH];
- fileHandle_t demofile;
- size_t len;
+ qhandle_t f;
if( !sv_mvd_autorecord->integer ) {
return;
@@ -179,24 +178,18 @@ static void dummy_record_f( void ) {
return;
}
- len = Q_concat( buffer, sizeof( buffer ), "demos/", Cmd_Argv( 1 ), ".mvd2", NULL );
- if( len >= sizeof( buffer ) ) {
- Com_EPrintf( "Oversize filename specified.\n" );
- return;
- }
-
- FS_FOpenFile( buffer, &demofile, FS_MODE_WRITE );
- if( !demofile ) {
- Com_EPrintf( "Couldn't open %s for writing\n", buffer );
+ f = FS_EasyOpenFile( buffer, sizeof( buffer ), FS_MODE_WRITE,
+ "demos/", Cmd_Argv( 1 ), ".mvd2" );
+ if( !f ) {
return;
}
if( !mvd_enable() ) {
- FS_FCloseFile( demofile );
+ FS_FCloseFile( f );
return;
}
- rec_start( demofile );
+ rec_start( f );
Com_Printf( "Auto-recording local MVD to %s\n", buffer );
}
@@ -2023,7 +2016,7 @@ static qboolean rec_allowed( void ) {
return qtrue;
}
-static void rec_start( fileHandle_t demofile ) {
+static void rec_start( qhandle_t demofile ) {
uint32_t magic;
mvd.recording = demofile;
@@ -2061,10 +2054,9 @@ Every entity, every playerinfo and every message will be recorded.
*/
static void SV_MvdRecord_f( void ) {
char buffer[MAX_OSPATH];
- fileHandle_t demofile;
- qboolean gzip = qfalse;
+ qhandle_t f;
+ unsigned mode = FS_MODE_WRITE;
int c;
- size_t len;
if( sv.state != ss_game ) {
#if USE_MVD_CLIENT
@@ -2086,7 +2078,7 @@ static void SV_MvdRecord_f( void ) {
Cmd_PrintHelp( o_record );
return;
case 'z':
- gzip = qtrue;
+ mode |= FS_FLAG_GZIP;
break;
default:
return;
@@ -2106,29 +2098,18 @@ static void SV_MvdRecord_f( void ) {
//
// open the demo file
//
- len = Q_concat( buffer, sizeof( buffer ), "demos/", cmd_optarg,
- gzip ? ".mvd2.gz" : ".mvd2", NULL );
- if( len >= sizeof( buffer ) ) {
- Com_EPrintf( "Oversize filename specified.\n" );
- return;
- }
-
- FS_FOpenFile( buffer, &demofile, FS_MODE_WRITE );
- if( !demofile ) {
- Com_EPrintf( "Couldn't open %s for writing\n", buffer );
+ f = FS_EasyOpenFile( buffer, sizeof( buffer ), mode,
+ "demos/", cmd_optarg, ".mvd2" );
+ if( !f ) {
return;
}
if( !mvd_enable() ) {
- FS_FCloseFile( demofile );
+ FS_FCloseFile( f );
return;
}
- if( gzip ) {
- FS_FilterFile( demofile );
- }
-
- rec_start( demofile );
+ rec_start( f );
Com_Printf( "Recording local MVD to %s\n", buffer );
}
diff --git a/source/sv_save.c b/source/sv_save.c
index 738365c..7b4f36f 100644
--- a/source/sv_save.c
+++ b/source/sv_save.c
@@ -30,17 +30,12 @@ SAVEGAME FILES
*/
static void write_binary_file( const char *name ) {
- fileHandle_t f;
+ qerror_t ret;
- FS_FOpenFile( name, &f, FS_MODE_WRITE );
- if( !f ) {
- Com_EPrintf( "%s: couldn't open %s\n", __func__, name );
- return;
+ ret = FS_WriteFile( name, msg_write.data, msg_write.cursize );
+ if( ret < 0 ) {
+ Com_EPrintf( "%s: couldn't write %s: %s\n", __func__, name, Q_ErrorString( ret ) );
}
-
- FS_Write( msg_write.data, msg_write.cursize, f );
-
- FS_FCloseFile( f );
}
static void write_server_file( qboolean autosave ) {
@@ -116,12 +111,12 @@ static void write_level_file( void ) {
static void read_binary_file( const char *name ) {
- fileHandle_t f;
- size_t len;
+ qhandle_t f;
+ ssize_t len;
len = FS_FOpenFile( name, &f, FS_MODE_READ|FS_TYPE_REAL|FS_PATH_GAME );
if( !f ) {
- Com_Error( ERR_DROP, "%s: couldn't open %s\n", __func__, name );
+ Com_Error( ERR_DROP, "%s: couldn't open %s: %s\n", __func__, name, Q_ErrorString( len ) );
}
if( len > MAX_MSGLEN ) {
@@ -248,7 +243,7 @@ void SV_Loadgame_f (void) {
// make sure the server.ssv file exists
Q_snprintf (name, sizeof(name), "save/%s/server.state", Cmd_Argv(1));
- if (FS_LoadFile( name, NULL ) == INVALID_LENGTH ) {
+ if (!FS_FileExists( name ) ) {
Com_Printf ("No such savegame: %s\n", name);
return;
}
diff --git a/source/sv_user.c b/source/sv_user.c
index 87db7f5..a8cfc67 100644
--- a/source/sv_user.c
+++ b/source/sv_user.c
@@ -561,13 +561,6 @@ static void SV_NextDownload_f( void ) {
}
-static void SV_DownloadFailed( void ) {
- MSG_WriteByte( svc_download );
- MSG_WriteShort( -1 );
- MSG_WriteByte( 0 );
- SV_ClientAddMessage( sv_client, MSG_RELIABLE|MSG_CLEAR );
-}
-
/*
==================
SV_BeginDownload_f
@@ -575,10 +568,13 @@ SV_BeginDownload_f
*/
static void SV_BeginDownload_f( void ) {
char name[MAX_QPATH];
- int downloadsize;
+ byte *download;
+ ssize_t downloadsize, maxdownloadsize, result;
int offset = 0;
cvar_t *allow;
int length;
+ unsigned flags;
+ qhandle_t f;
length = Q_ClearStr( name, Cmd_Argv( 1 ), sizeof( name ) );
Q_strlwr( name );
@@ -612,8 +608,7 @@ static void SV_BeginDownload_f( void ) {
// MUST be in a subdirectory
|| !strchr( name, '/' ) )
{
- SV_DownloadFailed();
- return;
+ goto fail1;
}
if( strncmp( name, "players/", 8 ) == 0 ) {
@@ -638,8 +633,7 @@ static void SV_BeginDownload_f( void ) {
if( !allow->integer ) {
Com_DPrintf( "Refusing download of %s to %s\n", name, sv_client->name );
- SV_DownloadFailed();
- return;
+ goto fail1;
}
if( sv_client->download ) {
@@ -647,32 +641,42 @@ static void SV_BeginDownload_f( void ) {
SV_CloseDownload( sv_client );
}
- downloadsize = FS_LoadFileEx( name, NULL, 0, TAG_SERVER );
-
- if( downloadsize == INVALID_LENGTH || downloadsize == 0
- // special check for maps, if it came from a pak file, don't allow
- // download ZOID
- || ( allow == allow_download_maps
- && allow_download_maps->integer < 2
- && FS_LastFileFromPak() ) )
- {
+ flags = FS_MODE_READ;
+
+ // special check for maps, if it came from a pak file, don't allow
+ // download ZOID
+ if( allow == allow_download_maps && allow->integer < 2 ) {
+ flags |= FS_TYPE_REAL;
+ }
+
+ downloadsize = FS_FOpenFile( name, &f, flags );
+ if( !f ) {
Com_DPrintf( "Couldn't download %s to %s\n", name, sv_client->name );
- SV_DownloadFailed();
- return;
+ goto fail1;
+ }
+
+ maxdownloadsize = MAX_LOADFILE;
+// if( sv_max_download_size->integer ) {
+// maxdownloadsize = Cvar_ClampInteger( sv_max_download_size, 1, MAX_LOADFILE );
+// }
+
+ if( downloadsize > maxdownloadsize ) {
+ Com_DPrintf( "Refusing oversize download of %s to %s\n", name, sv_client->name );
+ goto fail2;
}
if( offset > downloadsize ) {
Com_DPrintf( "Refusing download, %s has wrong version of %s (%d > %d)\n",
- sv_client->name, name, offset, downloadsize );
+ sv_client->name, name, offset, (int)downloadsize );
SV_ClientPrintf( sv_client, PRINT_HIGH, "File size differs from server.\n"
"Please delete the corresponding .tmp file from your system.\n" );
- SV_DownloadFailed();
- return;
+ goto fail2;
}
if( offset == downloadsize ) {
Com_DPrintf( "Refusing download, %s already has %s (%d bytes)\n",
sv_client->name, name, offset );
+ FS_FCloseFile( f );
MSG_WriteByte( svc_download );
MSG_WriteShort( 0 );
MSG_WriteByte( 100 );
@@ -680,14 +684,32 @@ static void SV_BeginDownload_f( void ) {
return;
}
- sv_client->downloadsize = FS_LoadFileEx( name,
- ( void ** )&sv_client->download, 0, TAG_SERVER );
+ download = SV_Malloc( downloadsize );
+ result = FS_Read( download, downloadsize, f );
+ if( result != downloadsize ) {
+ Com_DPrintf( "Couldn't download %s to %s\n", name, sv_client->name );
+ goto fail3;
+ }
+
+ sv_client->download = download;
+ sv_client->downloadsize = downloadsize;
sv_client->downloadcount = offset;
sv_client->downloadname = SV_CopyString( name );
Com_DPrintf( "Downloading %s to %s\n", name, sv_client->name );
SV_NextDownload_f();
+ return;
+
+fail3:
+ Z_Free( download );
+fail2:
+ FS_FCloseFile( f );
+fail1:
+ MSG_WriteByte( svc_download );
+ MSG_WriteShort( -1 );
+ MSG_WriteByte( 0 );
+ SV_ClientAddMessage( sv_client, MSG_RELIABLE|MSG_CLEAR );
}
static void SV_StopDownload_f( void ) {
diff --git a/source/sw_image.c b/source/sw_image.c
index 085fa60..daab6d7 100644
--- a/source/sw_image.c
+++ b/source/sw_image.c
@@ -74,28 +74,32 @@ image_t *IMG_LoadWAL( const char *name ) {
miptex_t *mt;
image_t *image;
size_t width, height, offset, endpos, filelen, size;
+ qerror_t ret;
filelen = FS_LoadFile( name, ( void ** )&mt );
if( !mt ) {
- return NULL;
+ // don't spam about missing images
+ if( filelen == Q_ERR_NOENT ) {
+ return NULL;
+ }
+ ret = filelen;
+ goto fail1;
}
- image = NULL;
-
width = LittleLong( mt->width );
height = LittleLong( mt->height );
offset = LittleLong( mt->offsets[0] );
if( width < 1 || height < 1 || width > MAX_TEXTURE_SIZE || height > MAX_TEXTURE_SIZE ) {
- Com_WPrintf( "LoadWAL: %s: bad dimensions\n", name );
- goto fail;
+ ret = Q_ERR_INVALID_FORMAT;
+ goto fail2;
}
size = width * height * ( 256 + 64 + 16 + 4 ) / 256;
endpos = offset + size;
if( endpos < offset || endpos > filelen ) {
- Com_WPrintf( "LoadWAL: %s: bad offset\n", name );
- goto fail;
+ ret = Q_ERR_BAD_EXTENT;
+ goto fail2;
}
image = IMG_Alloc( name );
@@ -112,10 +116,15 @@ image_t *IMG_LoadWAL( const char *name ) {
memcpy( image->pixels[0], ( byte * )mt + offset, size );
-fail:
FS_FreeFile( mt );
return image;
+
+fail2:
+ FS_FreeFile( mt );
+fail1:
+ Com_EPrintf( "Couldn't load %s: %s\n", name, Q_ErrorString( ret ) );
+ return NULL;
}
static void R_BuildGammaTable( void ) {
@@ -178,19 +187,27 @@ int R_IndexForColor( const color_t color ) {
}
static void R_Get16to8( void ) {
- fileHandle_t f;
- size_t r;
+ qhandle_t f;
+ ssize_t ret;
- FS_FOpenFile( "pics/16to8.dat", &f, FS_MODE_READ );
+ ret = FS_FOpenFile( "pics/16to8.dat", &f, FS_MODE_READ );
if( !f ) {
- Com_Error( ERR_FATAL, "Couldn't load pics/16to8.dat" );
+ goto fail;
}
- r = FS_Read( d_16to8table, sizeof( d_16to8table ), f );
- if( r != sizeof( d_16to8table ) ) {
- Com_Error( ERR_FATAL, "Malformed pics/16to8.dat" );
+
+ ret = FS_Read( d_16to8table, sizeof( d_16to8table ), f );
+ if( ret != sizeof( d_16to8table ) ) {
+ if( ret >= 0 ) {
+ ret = Q_ERR_UNEXPECTED_EOF;
+ }
+ goto fail;
}
FS_FCloseFile( f );
+ return;
+
+fail:
+ Com_Error( ERR_FATAL, "Couldn't load pics/16to8.dat: %s", Q_ErrorString( ret ) );
}
/*
diff --git a/source/sw_model.c b/source/sw_model.c
index 653cdfd..68e2e13 100644
--- a/source/sw_model.c
+++ b/source/sw_model.c
@@ -288,7 +288,7 @@ void R_BeginRegistration( const char *model ) {
Q_concat( fullname, sizeof( fullname ), "maps/", model, ".bsp", NULL );
D_FlushCaches ();
- bsp = BSP_Load( fullname );
+ BSP_Load( fullname, &bsp );
if( bsp == r_worldmodel ) {
mtexinfo_t *tex = bsp->texinfo;
int i;
diff --git a/source/sys_public.h b/source/sys_public.h
index 976dbc4..76280a8 100644
--- a/source/sys_public.h
+++ b/source/sys_public.h
@@ -55,9 +55,9 @@ void Sys_Quit( void ) q_noreturn;
void **Sys_ListFiles( const char *path, const char *extension,
int flags, size_t length, int *numFiles );
-struct file_info_s;
-qboolean Sys_GetPathInfo( const char *path, struct file_info_s *info );
-qboolean Sys_GetFileInfo( FILE *fp, struct file_info_s *info );
+struct file_info_s;
+qerror_t Sys_GetPathInfo( const char *path, struct file_info_s *info );
+qerror_t Sys_GetFileInfo( FILE *fp, struct file_info_s *info );
char *Sys_GetCurrentDirectory( void );
diff --git a/source/sys_unix.c b/source/sys_unix.c
index 0368a77..885a97b 100644
--- a/source/sys_unix.c
+++ b/source/sys_unix.c
@@ -35,7 +35,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <ctype.h>
#include <sys/wait.h>
#include <sys/mman.h>
-#include <errno.h>
#include <dirent.h>
#include <dlfcn.h>
#include <termios.h>
@@ -486,15 +485,15 @@ unsigned Sys_Milliseconds( void ) {
Sys_GetPathInfo
================
*/
-qboolean Sys_GetPathInfo( const char *path, file_info_t *info ) {
+qerror_t Sys_GetPathInfo( const char *path, file_info_t *info ) {
struct stat st;
if( stat( path, &st ) == -1 ) {
- return qfalse;
+ return Q_ERR(errno);
}
if( !S_ISREG( st.st_mode ) ) {
- return qfalse;
+ return Q_ERR_ISDIR;
}
if( info ) {
@@ -503,18 +502,18 @@ qboolean Sys_GetPathInfo( const char *path, file_info_t *info ) {
info->mtime = st.st_mtime;
}
- return qtrue;
+ return Q_ERR_SUCCESS;
}
-qboolean Sys_GetFileInfo( FILE *fp, file_info_t *info ) {
+qerror_t Sys_GetFileInfo( FILE *fp, file_info_t *info ) {
struct stat st;
if( fstat( fileno( fp ), &st ) == -1 ) {
- return qfalse;
+ return Q_ERR(errno);
}
if( !S_ISREG( st.st_mode ) ) {
- return qfalse;
+ return Q_ERR_ISDIR;
}
if( info ) {
@@ -523,7 +522,7 @@ qboolean Sys_GetFileInfo( FILE *fp, file_info_t *info ) {
info->mtime = st.st_mtime;
}
- return qtrue;
+ return Q_ERR_SUCCESS;
}
/*
diff --git a/source/ui_demos.c b/source/ui_demos.c
index e3a8130..ce2e8d7 100644
--- a/source/ui_demos.c
+++ b/source/ui_demos.c
@@ -145,7 +145,7 @@ fail:
static void WriteCache( void ) {
char buffer[MAX_OSPATH];
- fileHandle_t f;
+ qhandle_t f;
int i;
char *map, *pov;
demoEntry_t *e;
diff --git a/source/ui_playermodels.c b/source/ui_playermodels.c
index eb18d68..fbbae17 100644
--- a/source/ui_playermodels.c
+++ b/source/ui_playermodels.c
@@ -141,7 +141,7 @@ void PlayerModel_Load( void ) {
// verify the existence of tris.md2
Q_concat( scratch, sizeof( scratch ), "players/", dirnames[i], "/tris.md2", NULL );
- if( FS_LoadFile( scratch, NULL ) == INVALID_LENGTH ) {
+ if( !FS_FileExists( scratch ) ) {
continue;
}
diff --git a/source/ui_script.c b/source/ui_script.c
index 091a0f2..70035b3 100644
--- a/source/ui_script.c
+++ b/source/ui_script.c
@@ -297,10 +297,11 @@ static qboolean Parse_File( const char *path, int depth ) {
char *raw, *data, *p, *cmd;
int argc;
menuFrameWork_t *menu = NULL;
+ qerror_t ret;
- FS_LoadFile( path, ( void ** )&raw );
+ ret = FS_LoadFile( path, ( void ** )&raw );
if( !raw ) {
- Com_Printf( "Couldn't load %s\n", path );
+ Com_Printf( "Couldn't load %s: %s\n", path, Q_ErrorString( ret ) );
return qfalse;
}