mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-01-29 20:20:43 +00:00
First commit of an experimental low-level object system. At some point
GIB will be reworked to use it, and hopefully Rua will also so that the two languages can share objects, events, etc. Warning: This uses quite a few hacks and tortured macros, it might cause breakage.
This commit is contained in:
parent
37a64e59ab
commit
55181621bd
5 changed files with 458 additions and 2 deletions
|
@ -4,7 +4,7 @@ includedir = $(prefix)/include/QF
|
||||||
include_HEADERS = bspfile.h cbuf.h cdaudio.h checksum.h clip_hull.h cmd.h \
|
include_HEADERS = bspfile.h cbuf.h cdaudio.h checksum.h clip_hull.h cmd.h \
|
||||||
console.h crc.h csqc.h cvar.h dstring.h draw.h gib.h hash.h hl.h \
|
console.h crc.h csqc.h cvar.h dstring.h draw.h gib.h hash.h hl.h \
|
||||||
idparse.h image.h in_event.h info.h input.h joystick.h keys.h link.h \
|
idparse.h image.h in_event.h info.h input.h joystick.h keys.h link.h \
|
||||||
llist.h locs.h mathlib.h mdfour.h model.h modelgen.h msg.h pak.h \
|
llist.h locs.h mathlib.h mdfour.h model.h modelgen.h msg.h object.h pak.h \
|
||||||
pakfile.h pcx.h png.h plugin.h pr_comp.h pr_debug.h pr_obj.h progs.h \
|
pakfile.h pcx.h png.h plugin.h pr_comp.h pr_debug.h pr_obj.h progs.h \
|
||||||
qargs.h qdefs.h qendian.h qfplist.h qtypes.h quakefs.h quakeio.h render.h \
|
qargs.h qdefs.h qendian.h qfplist.h qtypes.h quakefs.h quakeio.h render.h \
|
||||||
riff.h screen.h sizebuf.h skin.h sound.h spritegn.h sys.h teamplay.h \
|
riff.h screen.h sizebuf.h skin.h sound.h spritegn.h sys.h teamplay.h \
|
||||||
|
|
106
include/QF/object.h
Normal file
106
include/QF/object.h
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
object.h
|
||||||
|
|
||||||
|
Provides a primitive object framework to back objects
|
||||||
|
in higher level languages of QF so that they can share
|
||||||
|
objects. For example, Ruamoko and GIB would be able to
|
||||||
|
pass String objects to each other, even if the higher
|
||||||
|
-level implementations of String in each language differ.
|
||||||
|
|
||||||
|
Copyright (C) 2003 Brian Koropoff
|
||||||
|
|
||||||
|
Author: Brian Koropoff
|
||||||
|
Date: November 28, 2003
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
Free Software Foundation, Inc.
|
||||||
|
59 Temple Place - Suite 330
|
||||||
|
Boston, MA 02111-1307, USA
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __object_h
|
||||||
|
#define __object_h
|
||||||
|
|
||||||
|
#include "QF/qtypes.h"
|
||||||
|
|
||||||
|
#define classDecl(name,extends,def) typedef struct name##_s {struct extends##_s base; def} name; extern Class * name##_class
|
||||||
|
#define superInit(cl, obj, ...) (cl##_class->parent->init ((obj), ##__VA_ARGS__))
|
||||||
|
#define new(cl, ...) ((void *) (cl##_class->abstract ? NULL : retain(cl##_class->init (Object_Create(cl##_class), ##__VA_ARGS__))))
|
||||||
|
#define newFloat(cl, ...) ((void *) (cl##_class->abstract ? NULL : cl##_class->init (Object_Create(cl##_class), ##__VA_ARGS__)))
|
||||||
|
#define methodCall(obj, m, ...) ((obj)->m(obj, ##__VA_ARGS__))
|
||||||
|
#define methodDecl(type, name, ...) (* name) (struct type##_s *self, ##__VA_ARGS__)
|
||||||
|
#define retain(obj) (Object_Retain((Object *)obj))
|
||||||
|
#define release(obj) (Object_Release((Object *)obj))
|
||||||
|
|
||||||
|
typedef struct Object_s {
|
||||||
|
struct Class_s *cl;
|
||||||
|
int refs;
|
||||||
|
struct String_s *(*toString)(struct Object_s *obj);
|
||||||
|
} Object;
|
||||||
|
#define OBJECT(o) ((Object *)(o))
|
||||||
|
|
||||||
|
|
||||||
|
typedef Object *(*Object_Init_t) (Object *obj, ...);
|
||||||
|
typedef void (*Object_Deinit_t) (Object *obj);
|
||||||
|
|
||||||
|
classDecl (Class, Object,
|
||||||
|
qboolean abstract;
|
||||||
|
unsigned int size;
|
||||||
|
const char *name;
|
||||||
|
struct Class_s *parent;
|
||||||
|
Object_Init_t init;
|
||||||
|
Object_Deinit_t deinit;
|
||||||
|
);
|
||||||
|
#define CLASS(o) ((Class *)(o))
|
||||||
|
|
||||||
|
classDecl (String, Object,
|
||||||
|
const char *str;
|
||||||
|
);
|
||||||
|
#define STRING(o) ((String *)(o))
|
||||||
|
|
||||||
|
classDecl (List, Object,
|
||||||
|
unsigned int count;
|
||||||
|
Object * methodDecl (List, get, unsigned int index);
|
||||||
|
qboolean methodDecl (List, add, Object *o);
|
||||||
|
);
|
||||||
|
#define LIST(o) ((List *)(o))
|
||||||
|
|
||||||
|
classDecl (ArrayList, List,
|
||||||
|
Object **elements;
|
||||||
|
);
|
||||||
|
#define ARRAYLIST(o) ((ArrayList *)(o))
|
||||||
|
|
||||||
|
classDecl (Number, Object,
|
||||||
|
int methodDecl (Number, intValue);
|
||||||
|
double methodDecl (Number, doubleValue);
|
||||||
|
);
|
||||||
|
#define NUMBER(o) ((Number *)(o))
|
||||||
|
|
||||||
|
classDecl (Integer, Number,
|
||||||
|
int value;
|
||||||
|
);
|
||||||
|
#define INTEGER(o) ((Integer *)(o))
|
||||||
|
|
||||||
|
Object *Object_Create (Class *cl);
|
||||||
|
void Object_Delete (Object *obj);
|
||||||
|
Object *Object_Retain (Object *obj);
|
||||||
|
Object *Object_Release (Object *obj);
|
||||||
|
|
||||||
|
void Object_Init (void);
|
||||||
|
|
||||||
|
#endif
|
|
@ -28,7 +28,7 @@ libQFutil_la_DEPENDENCIES= libasm.la
|
||||||
libQFutil_la_SOURCES= \
|
libQFutil_la_SOURCES= \
|
||||||
bspfile.c buildnum.c cbuf.c checksum.c cmd.c crc.c cvar.c dstring.c \
|
bspfile.c buildnum.c cbuf.c checksum.c cmd.c crc.c cvar.c dstring.c \
|
||||||
fendian.c getopt.c getopt1.c hash.c idparse.c info.c link.c llist.c \
|
fendian.c getopt.c getopt1.c hash.c idparse.c info.c link.c llist.c \
|
||||||
mathlib.c mdfour.c msg.c pakfile.c plugin.c qargs.c qendian.c \
|
mathlib.c mdfour.c msg.c object.c pakfile.c plugin.c qargs.c qendian.c \
|
||||||
qfplist.c quakefs.c quakeio.c riff.c sizebuf.c string.c sys.c \
|
qfplist.c quakefs.c quakeio.c riff.c sizebuf.c string.c sys.c \
|
||||||
va.c ver_check.c wad.c zone.c $(fnmatch)
|
va.c ver_check.c wad.c zone.c $(fnmatch)
|
||||||
|
|
||||||
|
|
346
libs/util/object.c
Normal file
346
libs/util/object.c
Normal file
|
@ -0,0 +1,346 @@
|
||||||
|
/*
|
||||||
|
object.c
|
||||||
|
|
||||||
|
Provides a primitive object framework to back objects
|
||||||
|
in higher level languages of QF so that they can share
|
||||||
|
objects. For example, Ruamoko and GIB would be able to
|
||||||
|
pass String objects to each other, even if the higher
|
||||||
|
-level implementations of String in each language differ.
|
||||||
|
|
||||||
|
Copyright (C) 2003 Brian Koropoff
|
||||||
|
|
||||||
|
Author: Brian Koropoff
|
||||||
|
Date: November 28, 2003
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
Free Software Foundation, Inc.
|
||||||
|
59 Temple Place - Suite 330
|
||||||
|
Boston, MA 02111-1307, USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static __attribute__ ((unused)) const char rcsid[] =
|
||||||
|
"$Id$";
|
||||||
|
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
# include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRINGS_H
|
||||||
|
# include <strings.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "QF/dstring.h"
|
||||||
|
#include "QF/sys.h"
|
||||||
|
#include "QF/object.h"
|
||||||
|
#include "QF/va.h"
|
||||||
|
|
||||||
|
static String *
|
||||||
|
Object_ToString_f (Object *self)
|
||||||
|
{
|
||||||
|
return newFloat(String, va("%s@%p", self->cl->name, self));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Object_Init_f (Object *self)
|
||||||
|
{
|
||||||
|
self->refs = -1; // Floating reference
|
||||||
|
self->toString = Object_ToString_f;
|
||||||
|
Sys_DPrintf("%s@%p initing...\n", self->cl->name, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Object_Deinit_f (Object *self)
|
||||||
|
{
|
||||||
|
Sys_DPrintf("%s@%p deiniting...\n", self->cl->name, self);
|
||||||
|
free (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String *
|
||||||
|
Class_ToString_f (Object *self)
|
||||||
|
{
|
||||||
|
return newFloat(String, CLASS(self)->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object *
|
||||||
|
Class_Init_f (Object *self, const char *name, unsigned int size, Class *parent, void *init, void *deinit, qboolean abstract)
|
||||||
|
{
|
||||||
|
superInit(Class, self);
|
||||||
|
CLASS(self)->name = strdup(name);
|
||||||
|
CLASS(self)->size = size;
|
||||||
|
CLASS(self)->parent = parent;
|
||||||
|
CLASS(self)->init = (Object_Init_t) init;
|
||||||
|
CLASS(self)->deinit = (Object_Deinit_t) deinit;
|
||||||
|
self->toString = Class_ToString_f;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Class_Deinit_f (Object *self) {
|
||||||
|
free ((void *)CLASS(self)->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String *
|
||||||
|
String_ToString_f (Object *self)
|
||||||
|
{
|
||||||
|
return STRING(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object *
|
||||||
|
String_Init_f (Object *self, const char *value)
|
||||||
|
{
|
||||||
|
superInit(String, self);
|
||||||
|
self->toString = String_ToString_f;
|
||||||
|
STRING(self)->str = strdup (value);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
String_Deinit_f (Object *self)
|
||||||
|
{
|
||||||
|
free((void *)STRING(self)->str);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object *
|
||||||
|
Object_Create (Class *cl)
|
||||||
|
{
|
||||||
|
Object *new = malloc (cl->size);
|
||||||
|
new->cl = cl;
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Object_Delete (Object *obj)
|
||||||
|
{
|
||||||
|
Class *c;
|
||||||
|
for (c = obj->cl; c; c = c->parent)
|
||||||
|
c->deinit (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object *
|
||||||
|
Object_Retain (Object *obj)
|
||||||
|
{
|
||||||
|
if (obj->refs == -1) {
|
||||||
|
obj->refs = 1;
|
||||||
|
} else {
|
||||||
|
obj->refs++;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object *
|
||||||
|
Object_Release (Object *obj)
|
||||||
|
{
|
||||||
|
if (obj->refs == -1)
|
||||||
|
Sys_Error ("%s@%p with floating reference released.", obj->cl->name, obj);
|
||||||
|
if (--obj->refs < 1) {
|
||||||
|
Object_Delete (obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String *
|
||||||
|
List_ToString_f (Object *self)
|
||||||
|
{
|
||||||
|
List *list = LIST(self);
|
||||||
|
String *str;
|
||||||
|
unsigned int i;
|
||||||
|
dstring_t *dstr = dstring_newstr();
|
||||||
|
|
||||||
|
dstring_appendstr (dstr, "{");
|
||||||
|
|
||||||
|
for (i = 0; i < list->count; i++) {
|
||||||
|
str = methodCall(methodCall(list, get, i), toString);
|
||||||
|
retain(str);
|
||||||
|
dstring_appendstr (dstr, str->str);
|
||||||
|
release(str);
|
||||||
|
if (i < list->count - 1)
|
||||||
|
dstring_appendstr (dstr, ", ");
|
||||||
|
}
|
||||||
|
dstring_appendstr (dstr, "}");
|
||||||
|
str = newFloat(String, dstr->str);
|
||||||
|
dstring_delete (dstr);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object *
|
||||||
|
List_Init_f (Object *self)
|
||||||
|
{
|
||||||
|
superInit(List, self);
|
||||||
|
LIST(self)->count = 0;
|
||||||
|
LIST(self)->get = NULL;
|
||||||
|
LIST(self)->add = NULL;
|
||||||
|
self->toString = List_ToString_f;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
List_Deinit_f (Object *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object *
|
||||||
|
ArrayList_Get_f (List *self, unsigned int index)
|
||||||
|
{
|
||||||
|
if (index >= self->count)
|
||||||
|
return NULL;
|
||||||
|
return ARRAYLIST(self)->elements[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
static qboolean
|
||||||
|
ArrayList_Add_f (List *self, Object *o)
|
||||||
|
{
|
||||||
|
self->count++;
|
||||||
|
ARRAYLIST(self)->elements = realloc (ARRAYLIST(self)->elements, self->count);
|
||||||
|
ARRAYLIST(self)->elements[self->count-1] = o;
|
||||||
|
retain (o);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object *
|
||||||
|
ArrayList_Init_f (Object *self)
|
||||||
|
{
|
||||||
|
superInit (ArrayList, self);
|
||||||
|
ARRAYLIST(self)->elements = NULL;
|
||||||
|
LIST(self)->get = ArrayList_Get_f;
|
||||||
|
LIST(self)->add = ArrayList_Add_f;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ArrayList_Deinit_f (Object *self)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
Object **elements = ARRAYLIST(self)->elements;
|
||||||
|
|
||||||
|
for (i = 0; i < LIST(self)->count; i++)
|
||||||
|
release (elements[i]);
|
||||||
|
if (elements)
|
||||||
|
free (elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object *
|
||||||
|
Number_Init_f (Object *self)
|
||||||
|
{
|
||||||
|
superInit (Number, self);
|
||||||
|
NUMBER(self)->intValue = NULL;
|
||||||
|
NUMBER(self)->doubleValue = NULL;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Number_Deinit_f (Object *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static String *
|
||||||
|
Integer_ToString_f (Object *self)
|
||||||
|
{
|
||||||
|
return newFloat(String, va("%i", INTEGER(self)->value));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
Integer_IntValue_f (Number *self)
|
||||||
|
{
|
||||||
|
return INTEGER(self)->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double
|
||||||
|
Integer_DoubleValue_f (Number *self)
|
||||||
|
{
|
||||||
|
return (double) INTEGER(self)->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object *
|
||||||
|
Integer_Init_f (Object *self, int value)
|
||||||
|
{
|
||||||
|
superInit (Integer, self);
|
||||||
|
INTEGER(self)->value = value;
|
||||||
|
NUMBER(self)->intValue = Integer_IntValue_f;
|
||||||
|
NUMBER(self)->doubleValue = Integer_DoubleValue_f;
|
||||||
|
self->toString = Integer_ToString_f;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Integer_Deinit_f (Object *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Class *Object_class;
|
||||||
|
Class *Class_class;
|
||||||
|
Class *String_class;
|
||||||
|
Class *List_class;
|
||||||
|
Class *ArrayList_class;
|
||||||
|
Class *Number_class;
|
||||||
|
Class *Integer_class;
|
||||||
|
|
||||||
|
static void
|
||||||
|
Object_Test (void)
|
||||||
|
{
|
||||||
|
List *l = new(ArrayList);
|
||||||
|
String *str;
|
||||||
|
|
||||||
|
methodCall(l, add, newFloat(Integer, 5));
|
||||||
|
methodCall(l, add, newFloat(String, "Daisy"));
|
||||||
|
methodCall(l, add, newFloat(Integer, 42));
|
||||||
|
|
||||||
|
str = methodCall(OBJECT(l), toString);
|
||||||
|
retain(str);
|
||||||
|
Sys_DPrintf("List: %s\n", str->str);
|
||||||
|
release(str);
|
||||||
|
release(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Object_Init (void)
|
||||||
|
{
|
||||||
|
/* There is somewhat of a chicken and egg problem
|
||||||
|
here.
|
||||||
|
*/
|
||||||
|
Object_class = malloc (sizeof (struct Class_s));
|
||||||
|
Class_class = malloc (sizeof (struct Class_s));
|
||||||
|
OBJECT(Object_class)->cl = Class_class;
|
||||||
|
OBJECT(Class_class)->cl = Class_class;
|
||||||
|
Class_class->parent = Object_class;
|
||||||
|
Object_class->init = (Object_Init_t) Object_Init_f;
|
||||||
|
|
||||||
|
Class_Init_f (OBJECT(Object_class), "Object", sizeof(Object), NULL, Object_Init_f, Object_Deinit_f, true);
|
||||||
|
Class_Init_f (OBJECT(Class_class), "Class", sizeof(Class), Object_class, Class_Init_f, Class_Deinit_f, false);
|
||||||
|
retain(Object_class);
|
||||||
|
retain(Class_class);
|
||||||
|
/* Phew... Object and Class are now bootstrapped,
|
||||||
|
classes can now be created by instantiating
|
||||||
|
Class
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Create String class normally */
|
||||||
|
String_class = new(Class, "String", sizeof(String), Object_class, String_Init_f, String_Deinit_f, false);
|
||||||
|
/* Etc... */
|
||||||
|
List_class = new(Class, "List", sizeof(List), Object_class, List_Init_f, List_Deinit_f, true);
|
||||||
|
ArrayList_class = new(Class, "ArrayList", sizeof(ArrayList), List_class, ArrayList_Init_f, ArrayList_Deinit_f, false);
|
||||||
|
Number_class = new(Class, "Number", sizeof(Number), Object_class, Number_Init_f, Number_Deinit_f, true);
|
||||||
|
Integer_class = new(Class, "Integer", sizeof(Integer), Number_class, Integer_Init_f, Integer_Deinit_f, false);
|
||||||
|
|
||||||
|
/* Run a test! */
|
||||||
|
Object_Test();
|
||||||
|
}
|
|
@ -71,6 +71,7 @@ static __attribute__ ((unused)) const char rcsid[] =
|
||||||
#include "QF/dstring.h"
|
#include "QF/dstring.h"
|
||||||
#include "QF/model.h"
|
#include "QF/model.h"
|
||||||
#include "QF/msg.h"
|
#include "QF/msg.h"
|
||||||
|
#include "QF/object.h"
|
||||||
#include "QF/plugin.h"
|
#include "QF/plugin.h"
|
||||||
#include "QF/qargs.h"
|
#include "QF/qargs.h"
|
||||||
#include "QF/quakefs.h"
|
#include "QF/quakefs.h"
|
||||||
|
@ -2496,6 +2497,9 @@ SV_Init (void)
|
||||||
Cmd_StuffCmds (sv_cbuf);
|
Cmd_StuffCmds (sv_cbuf);
|
||||||
Cbuf_Execute_Sets (sv_cbuf);
|
Cbuf_Execute_Sets (sv_cbuf);
|
||||||
|
|
||||||
|
// snax: Init experimental object system and run a test
|
||||||
|
Object_Init();
|
||||||
|
|
||||||
// execute the global configuration file if it exists
|
// execute the global configuration file if it exists
|
||||||
// would have been nice if Cmd_Exec_f could have been used, but it
|
// would have been nice if Cmd_Exec_f could have been used, but it
|
||||||
// only reads from within the quake file system, and changing that is
|
// only reads from within the quake file system, and changing that is
|
||||||
|
|
Loading…
Reference in a new issue