From ea076a96448626f07190e8d37b9089859e68ae33 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Thu, 26 Feb 2026 00:32:51 +0200 Subject: [PATCH 1/7] update shared from main project --- src/g_main.c | 23 +- src/g_spawn.c | 18 +- src/header/game.h | 91 ++++-- src/header/shared.h | 404 +++++++++++++++++------ src/shared/shared.c | 758 +++++++++++++++++++++++++++++++------------- 5 files changed, 931 insertions(+), 363 deletions(-) diff --git a/src/g_main.c b/src/g_main.c index e4ff999..13ed634 100644 --- a/src/g_main.c +++ b/src/g_main.c @@ -106,13 +106,9 @@ ShutdownGame(void) } /* -================= -GetGameAPI - -Returns a pointer to the structure with all entry points -and global variables -================= -*/ + * Returns a pointer to the structure with + * all entry points and global variables + */ Q2_DLL_EXPORTED game_export_t * GetGameAPI(game_import_t *import) { @@ -145,7 +141,7 @@ GetGameAPI(game_import_t *import) } void -Sys_Error(char *error, ...) +Sys_Error(const char *error, ...) { va_list argptr; char text[1024]; @@ -154,11 +150,11 @@ Sys_Error(char *error, ...) vsprintf(text, error, argptr); va_end(argptr); - gi.error(ERR_FATAL, "%s", text); + gi.error("%s", text); } void -Com_Printf(char *msg, ...) +Com_Printf(const char *msg, ...) { va_list argptr; char text[1024]; @@ -179,7 +175,7 @@ ClientEndServerFrames(void) edict_t *ent; /* calc the player views now that all - pushing and damage has been added */ + pushing and damage has been added */ for (i = 0; i < maxclients->value; i++) { ent = g_edicts + 1 + i; @@ -201,6 +197,11 @@ CreateTargetChangeLevel(char *map) { edict_t *ent; + if (!map) + { + return NULL; + } + ent = G_Spawn(); ent->classname = "target_changelevel"; Com_sprintf(level.nextmap, sizeof(level.nextmap), "%s", map); diff --git a/src/g_spawn.c b/src/g_spawn.c index 7529126..b67f889 100644 --- a/src/g_spawn.c +++ b/src/g_spawn.c @@ -270,7 +270,7 @@ ED_CallSpawn(edict_t *ent) } if (!strcmp(item->classname, ent->classname)) - { + { /* found it */ SpawnItem(ent, item); return; @@ -281,7 +281,7 @@ ED_CallSpawn(edict_t *ent) for (s = spawns; s->name; s++) { if (!strcmp(s->name, ent->classname)) - { + { /* found it */ s->spawn(ent); return; @@ -331,8 +331,8 @@ ED_NewString(char *string) * Takes a key/value pair and sets * the binary values in an edict */ -void -ED_ParseField(char *key, char *value, edict_t *ent) +static void +ED_ParseField(char *key, const char *value, edict_t *ent) { field_t *f; byte *b; @@ -342,7 +342,7 @@ ED_ParseField(char *key, char *value, edict_t *ent) for (f = fields; f->name; f++) { if (!Q_stricmp(f->name, key)) - { + { /* found it */ if (f->flags & FFL_SPAWNTEMP) { @@ -399,7 +399,6 @@ ED_ParseEdict(char *data, edict_t *ent) { qboolean init; char keyname[256]; - char *com_token; init = false; memset(&st, 0, sizeof(st)); @@ -407,6 +406,8 @@ ED_ParseEdict(char *data, edict_t *ent) /* go through all the dictionary pairs */ while (1) { + const char *com_token; + /* parse key */ com_token = COM_Parse(&data); @@ -534,7 +535,6 @@ SpawnEntities(char *mapname, char *entities, char *spawnpoint) { edict_t *ent; int inhibit; - char *com_token; int i; float skill_level; @@ -577,6 +577,8 @@ SpawnEntities(char *mapname, char *entities, char *spawnpoint) /* parse ents */ while (1) { + const char *com_token; + /* parse the opening brace */ com_token = COM_Parse(&entities); @@ -609,7 +611,7 @@ SpawnEntities(char *mapname, char *entities, char *spawnpoint) ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; } - /* remove things (except the world) from + /* remove things (except the world) from different skill levels or deathmatch */ if (ent != g_edicts) { diff --git a/src/header/game.h b/src/header/game.h index 15414f2..41d8e1c 100644 --- a/src/header/game.h +++ b/src/header/game.h @@ -39,19 +39,19 @@ #define GAME_API_VERSION 3 -/* edict->svflags */ -#define SVF_NOCLIENT 0x00000001 /* don't send entity to clients, even if it has effects */ -#define SVF_DEADMONSTER 0x00000002 /* treat as CONTENTS_DEADMONSTER for collision */ -#define SVF_MONSTER 0x00000004 /* treat as CONTENTS_MONSTER for collision */ -#define SVF_PROJECTILE 0x00000008 /* entity is simple projectile, used for network optimization */ +#define SVF_NOCLIENT 0x00000001 /* don't send entity to clients, even if it has effects */ +#define SVF_DEADMONSTER 0x00000002 /* treat as CONTENTS_DEADMONSTER for collision */ +#define SVF_MONSTER 0x00000004 /* treat as CONTENTS_MONSTER for collision */ +#define SVF_PROJECTILE 0x00000008 /* entity is simple projectile, used for network optimization */ + +#define MAX_ENT_CLUSTERS 16 -/* edict->solid values */ typedef enum { - SOLID_NOT, /* no interaction with other objects */ - SOLID_TRIGGER, /* only touch when inside, after moving */ - SOLID_BBOX, /* touch on edge */ - SOLID_BSP /* bsp clip, touch on edge */ + SOLID_NOT, /* no interaction with other objects */ + SOLID_TRIGGER, /* only touch when inside, after moving */ + SOLID_BBOX, /* touch on edge */ + SOLID_BSP /* bsp clip, touch on edge */ } solid_t; /* =============================================================== */ @@ -62,7 +62,6 @@ typedef struct link_s struct link_s *prev, *next; } link_t; -#define MAX_ENT_CLUSTERS 16 typedef struct edict_s edict_t; typedef struct gclient_s gclient_t; @@ -73,8 +72,8 @@ struct gclient_s { player_state_t ps; /* communicated by server to clients */ int ping; - /* the game dll can add anything it wants after */ - /* this point in the structure */ + /* the game dll can add anything it wants + after this point in the structure */ }; struct edict_s @@ -91,14 +90,15 @@ struct edict_s int headnode; /* unused if num_clusters != -1 */ int areanum, areanum2; - /* ================================ */ - - int svflags; /* SVF_NOCLIENT, SVF_DEADMONSTER, SVF_MONSTER, etc */ + int svflags; /* SVF_NOCLIENT, SVF_DEADMONSTER, SVF_MONSTER, etc */ vec3_t mins, maxs; vec3_t absmin, absmax, size; solid_t solid; int clipmask; edict_t *owner; + + /* the game dll can add anything it wants + after this point in the structure */ }; #endif /* GAME_INCLUDE */ @@ -109,20 +109,25 @@ struct edict_s typedef struct { /* special messages */ - void (*bprintf)(int printlevel, char *fmt, ...); - void (*dprintf)(char *fmt, ...); - void (*cprintf)(edict_t *ent, int printlevel, char *fmt, ...); - void (*centerprintf)(edict_t *ent, char *fmt, ...); + void (*bprintf)(int printlevel, const char *fmt, ...); + void (*dprintf)(const char *fmt, ...); + void (*cprintf)(edict_t *ent, int printlevel, const char *fmt, ...); + void (*centerprintf)(edict_t *ent, const char *fmt, ...); void (*sound)(edict_t *ent, int channel, int soundindex, float volume, float attenuation, float timeofs); void (*positioned_sound)(vec3_t origin, edict_t *ent, int channel, int soundinedex, float volume, float attenuation, float timeofs); + /* config strings hold all the index strings, the lightstyles, + and misc data like the sky definition and cdtrack. + All of the current configstrings are sent to clients when + they connect, and changes are sent to all connected clients. */ void (*configstring)(int num, char *string); - void (*error)(char *fmt, ...); + YQ2_ATTR_NORETURN_FUNCPTR void (*error)(const char *fmt, ...); - /* the *index functions create configstrings and some internal server state */ + /* the *index functions create configstrings + and some internal server state */ int (*modelindex)(char *name); int (*soundindex)(char *name); int (*imageindex)(char *name); @@ -138,11 +143,14 @@ typedef struct void (*SetAreaPortalState)(int portalnum, qboolean open); qboolean (*AreasConnected)(int area1, int area2); + /* an entity will never be sent to a client or used for collision + if it is not passed to linkentity. If the size, position, or + solidity changes, it must be relinked. */ void (*linkentity)(edict_t *ent); - void (*unlinkentity)(edict_t *ent); /* call before removing an interactive edict */ + void (*unlinkentity)(edict_t *ent); /* call before removing an interactive edict */ int (*BoxEdicts)(vec3_t mins, vec3_t maxs, edict_t **list, int maxcount, int areatype); - void (*Pmove)(pmove_t *pmove); /* player movement code common with client prediction */ + void (*Pmove)(pmove_t *pmove); /* player movement code common with client prediction */ /* network messaging */ void (*multicast)(vec3_t origin, multicast_t to); @@ -153,8 +161,8 @@ typedef struct void (*WriteLong)(int c); void (*WriteFloat)(float f); void (*WriteString)(char *s); - void (*WritePosition)(vec3_t pos); /* some fractional bits */ - void (*WriteDir)(vec3_t pos); /* single byte encoded, very coarse */ + void (*WritePosition)(vec3_t pos); /* some fractional bits */ + void (*WriteDir)(vec3_t pos); /* single byte encoded, very coarse */ void (*WriteAngle)(float f); /* managed memory allocation */ @@ -163,16 +171,19 @@ typedef struct void (*FreeTags)(int tag); /* console variable interaction */ - cvar_t *(*cvar)(char *var_name, char *value, int flags); - cvar_t *(*cvar_set)(char *var_name, char *value); - cvar_t *(*cvar_forceset)(char *var_name, char *value); + cvar_t *(*cvar)(const char *var_name, const char *value, int flags); + cvar_t *(*cvar_set)(const char *var_name, const char *value); + cvar_t *(*cvar_forceset)(const char *var_name, char *value); /* ClientCommand and ServerCommand parameter access */ int (*argc)(void); char *(*argv)(int n); - char *(*args)(void); /* concatenation of all argv >= 1 */ + char *(*args)(void); /* concatenation of all argv >= 1 */ + /* add commands to the server console as if + they were typed in for map changing, etc */ void (*AddCommandString)(char *text); + void (*DebugGraph)(float value, int color); } game_import_t; @@ -181,14 +192,25 @@ typedef struct { int apiversion; + /* the init function will only be called when a game starts, + not each time a level is loaded. Persistant data for clients + and the server can be allocated in init */ void (*Init)(void); void (*Shutdown)(void); /* each new level entered will cause a call to SpawnEntities */ void (*SpawnEntities)(char *mapname, char *entstring, char *spawnpoint); + /* Read/Write Game is for storing persistant cross level information + about the world state and the clients. + WriteGame is called every time a level is exited. + ReadGame is called on a loadgame. */ void (*WriteGame)(char *filename, qboolean autosave); void (*ReadGame)(char *filename); + + /* ReadLevel is called after the default + map information has been loaded with + SpawnEntities */ void (*WriteLevel)(char *filename); void (*ReadLevel)(char *filename); @@ -200,9 +222,18 @@ typedef struct void (*ClientThink)(edict_t *ent, usercmd_t *cmd); void (*RunFrame)(void); + + /* ServerCommand will be called when an "sv " + command is issued on the server console. The game can + issue gi.argc() / gi.argv() commands to get the rest + of the parameters */ void (*ServerCommand)(void); /* global variables shared between game and server */ + + /* The edict array is allocated in the game dll so it + can vary in size from one game to another. + The size will be fixed when ge->Init() is called */ struct edict_s *edicts; int edict_size; int num_edicts; /* current number, <= max_edicts */ diff --git a/src/header/shared.h b/src/header/shared.h index 74b2209..1c8215a 100644 --- a/src/header/shared.h +++ b/src/header/shared.h @@ -20,7 +20,9 @@ * ======================================================================= * * This is the main header file shared between client, renderer, server - * and the game. + * and the game. Do NOT edit this file unless you know what you're + * doing. Changes here may break the client <-> renderer <-> server + * <-> game API, leading to problems with mods! * * ======================================================================= */ @@ -44,14 +46,101 @@ typedef unsigned char byte; #define NULL ((void *)0) #endif +// stuff to align variables/arrays and for noreturn +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L // C11 or newer + #define YQ2_ALIGNAS_SIZE(SIZE) _Alignas(SIZE) + #define YQ2_ALIGNAS_TYPE(TYPE) _Alignas(TYPE) + // must be used as prefix (YQ2_ATTR_NORETURN void bla();)! + #define YQ2_ATTR_NORETURN _Noreturn + #define YQ2_STATIC_ASSERT(C, M) _Static_assert((C), M) + #if defined(__GNUC__) + #define YQ2_ATTR_MALLOC __attribute__ ((__malloc__)) + #define YQ2_ATTR_INLINE __attribute__((always_inline)) inline + #define YQ2_ATTR_RETURNS_NONNULL __attribute__ ((returns_nonnull)) + #elif defined(_MSC_VER) + #define YQ2_ATTR_MALLOC __declspec(restrict) + #define YQ2_ATTR_INLINE __forceinline + #define YQ2_ATTR_RETURNS_NONNULL + #else + // no equivalent per see + #define YQ2_ATTR_MALLOC + #define YQ2_ATTR_INLINE inline + #define YQ2_ATTR_RETURNS_NONNULL + #endif +#elif defined(__GNUC__) // GCC and clang should support this attribute + #define YQ2_ALIGNAS_SIZE(SIZE) __attribute__(( __aligned__(SIZE) )) + #define YQ2_ALIGNAS_TYPE(TYPE) __attribute__(( __aligned__(__alignof__(TYPE)) )) + // must be used as prefix (YQ2_ATTR_NORETURN void bla();)! + #define YQ2_ATTR_NORETURN __attribute__ ((noreturn)) + #define YQ2_ATTR_RETURNS_NONNULL __attribute__ ((returns_nonnull)) + #define YQ2_ATTR_MALLOC __attribute__ ((__malloc__)) + #define YQ2_ATTR_INLINE __attribute__((always_inline)) inline + // GCC supports this extension since 4.6 + #define YQ2_STATIC_ASSERT(C, M) _Static_assert((C), M) +#elif defined(_MSC_VER) + // Note: We prefer VS2019 16.8 or newer in C11 mode (/std:c11), + // then the __STDC_VERSION__ >= 201112L case above is used + + #define YQ2_ALIGNAS_SIZE(SIZE) __declspec(align(SIZE)) + // FIXME: for some reason, the following line doesn't work + //#define YQ2_ALIGNAS_TYPE( TYPE ) __declspec(align(__alignof(TYPE))) + + #ifdef _WIN64 // (hopefully) good enough workaround + #define YQ2_ALIGNAS_TYPE(TYPE) __declspec(align(8)) + #else // 32bit + #define YQ2_ALIGNAS_TYPE(TYPE) __declspec(align(4)) + #endif // _WIN64 + + // must be used as prefix (YQ2_ATTR_NORETURN void bla();)! + #define YQ2_ATTR_NORETURN __declspec(noreturn) + #define YQ2_ATTR_RETURNS_NONNULL + #define YQ2_ATTR_MALLOC __declspec(restrict) + #define YQ2_ATTR_INLINE __forceinline + #define YQ2_STATIC_ASSERT(C, M) assert((C) && M) +#else + #warning "Please add a case for your compiler here to align correctly" + #define YQ2_ALIGNAS_SIZE(SIZE) + #define YQ2_ALIGNAS_TYPE(TYPE) + #define YQ2_ATTR_NORETURN + #define YQ2_ATTR_RETURNS_NONNULL + #define YQ2_ATTR_MALLOC + #define YQ2_ATTR_INLINE inline + #define YQ2_STATIC_ASSERT(C, M) assert((C) && M) +#endif + +#if defined(__GNUC__) + /* ISO C11 _Noreturn can't be attached to function pointers, so + * use the gcc/clang-specific version for function pointers, even + * in C11 mode. MSVC __declspec(noreturn) seems to have the same + * restriction as _Noreturn so can't be used here either. */ + #define YQ2_ATTR_NORETURN_FUNCPTR __attribute__ ((noreturn)) +#else + #define YQ2_ATTR_NORETURN_FUNCPTR /* nothing */ +#endif + +/* Calculate length of a static array + * Example: for (i = 0; i < ARRLEN(arr); i++) + */ +#define ARRLEN(a) (sizeof(a) / sizeof(*a)) + +/* Calculate a pointer to the end of a static array + * Example: for (i = arr; i < ARREND(arr); i++) + */ +#define ARREND(a) &a[ARRLEN(a)] + /* angle indexes */ #define PITCH 0 /* up / down */ #define YAW 1 /* left / right */ #define ROLL 2 /* fall over */ -#define MAX_STRING_CHARS 1024 /* max length of a string passed to Cmd_TokenizeString */ +#define Q_min(a, b) (((a) < (b)) ? (a) : (b)) +#define Q_max(a, b) (((a) > (b)) ? (a) : (b)) +#define Q_clamp(x, l, u) ((l) > (x) ? (l) : (x) > (u) ? (u) : (x)) +#define Q_signf(x) ((x) < 0.0f ? -1.0f : 1.0f) + +#define MAX_STRING_CHARS 2048 /* max length of a string passed to Cmd_TokenizeString */ #define MAX_STRING_TOKENS 80 /* max tokens resulting from Cmd_TokenizeString */ -#define MAX_TOKEN_CHARS 128 /* max length of an individual token */ +#define MAX_TOKEN_CHARS 1024 /* max length of an individual token */ #define MAX_QPATH 64 /* max length of a quake game pathname */ @@ -91,9 +180,7 @@ typedef unsigned char byte; #define PRINTF_ATTR(FMT, VARGS) __attribute__((format(printf, FMT , VARGS ))) #endif -/* */ /* per-level limits */ -/* */ #define MAX_CLIENTS 256 /* absolute limit */ #define MAX_EDICTS 1024 /* must change protocol to increase more */ #define MAX_LIGHTSTYLES 256 @@ -139,6 +226,7 @@ typedef enum typedef float vec_t; typedef vec_t vec3_t[3]; typedef vec_t vec5_t[5]; +typedef float quat_t[4]; // x, y, z, w typedef int fixed4_t; typedef int fixed8_t; @@ -156,43 +244,57 @@ extern vec3_t vec3_origin; #define IS_NAN(x) (((*(int *)&x) & nanmask) == nanmask) +// FIXME: use int instead of long, it's only used with int anyway? #define Q_ftol(f) (long)(f) #define DotProduct(x, y) (x[0] * y[0] + x[1] * y[1] + x[2] * y[2]) -#define VectorSubtract(a, b, c) (c[0] = a[0] - b[0], c[1] = a[1] - b[1], c[2] = \ - a[2] - b[2]) -#define VectorAdd(a, b, c) (c[0] = a[0] + b[0], c[1] = a[1] + b[1], c[2] = \ - a[2] + b[2]) +#define VectorSubtract(a, b, c) \ + (c[0] = a[0] - b[0], c[1] = a[1] - b[1], c[2] = \ + a[2] - b[2]) +#define VectorAdd(a, b, c) \ + (c[0] = a[0] + b[0], c[1] = a[1] + b[1], c[2] = \ + a[2] + b[2]) #define VectorCopy(a, b) (b[0] = a[0], b[1] = a[1], b[2] = a[2]) #define VectorClear(a) (a[0] = a[1] = a[2] = 0) #define VectorNegate(a, b) (b[0] = -a[0], b[1] = -a[1], b[2] = -a[2]) #define VectorSet(v, x, y, z) (v[0] = (x), v[1] = (y), v[2] = (z)) -void VectorMA(vec3_t veca, float scale, vec3_t vecb, vec3_t vecc); +void VectorMA(const vec3_t veca, float scale, const vec3_t vecb, vec3_t vecc); /* just in case you do't want to use the macros */ -vec_t _DotProduct(vec3_t v1, vec3_t v2); -void _VectorSubtract(vec3_t veca, vec3_t vecb, vec3_t out); -void _VectorAdd(vec3_t veca, vec3_t vecb, vec3_t out); -void _VectorCopy(vec3_t in, vec3_t out); +vec_t _DotProduct(const vec3_t v1, const vec3_t v2); +void _VectorSubtract(const vec3_t veca, const vec3_t vecb, vec3_t out); +void _VectorAdd(const vec3_t veca, const vec3_t vecb, vec3_t out); +void _VectorCopy(const vec3_t in, vec3_t out); void ClearBounds(vec3_t mins, vec3_t maxs); -void AddPointToBounds(vec3_t v, vec3_t mins, vec3_t maxs); -int VectorCompare(vec3_t v1, vec3_t v2); -vec_t VectorLength(vec3_t v); -void CrossProduct(vec3_t v1, vec3_t v2, vec3_t cross); +void AddPointToBounds(const vec3_t v, vec3_t mins, vec3_t maxs); +void ClosestPointOnBounds(const vec3_t p, const vec3_t amin, const vec3_t amax, vec3_t out); +qboolean IsZeroVector(vec3_t v); +int VectorCompare(const vec3_t v1, const vec3_t v2); +vec_t VectorLengthSquared(vec3_t v); +vec_t VectorLength(const vec3_t v); +void CrossProduct(const vec3_t v1, const vec3_t v2, vec3_t cross); vec_t VectorNormalize(vec3_t v); /* returns vector length */ -vec_t VectorNormalize2(vec3_t v, vec3_t out); +vec_t VectorNormalize2(const vec3_t v, vec3_t out); void VectorInverse(vec3_t v); -void VectorScale(vec3_t in, vec_t scale, vec3_t out); +void VectorInverse2(const vec3_t v, vec3_t out); +void VectorScale(const vec3_t in, const vec_t scale, vec3_t out); +void VectorLerp(const vec3_t v1, const vec3_t v2, const vec_t factor, vec3_t out); +void VectorToQuat(const vec3_t v, quat_t out); +void QuatInverse(const quat_t q, quat_t out); +void QuatMultiply(const quat_t q1, const quat_t q2, quat_t out); +void QuatAngleAxis(const vec3_t v, float angle, quat_t out); +void RotateVectorByUnitQuat(vec3_t v, quat_t q_unit); +float Q_magnitude(float x, float y); int Q_log2(int val); -void R_ConcatRotations(float in1[3][3], float in2[3][3], float out[3][3]); -void R_ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4]); +void R_ConcatRotations(const float in1[3][3], const float in2[3][3], float out[3][3]); +void R_ConcatTransforms(const float in1[3][4], const float in2[3][4], float out[3][4]); -void AngleVectors(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); -void AngleVectors2(vec3_t value1, vec3_t angles); -int BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, struct cplane_s *plane); +void AngleVectors(const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); +void AngleVectors2(const vec3_t value1, vec3_t angles); +int BoxOnPlaneSide(const vec3_t emins, const vec3_t emaxs, const struct cplane_s *plane); float anglemod(float a); float LerpAngle(float a1, float a2, float frac); @@ -214,33 +316,83 @@ float LerpAngle(float a1, float a2, float frac); void ProjectPointOnPlane(vec3_t dst, const vec3_t p, const vec3_t normal); void PerpendicularVector(vec3_t dst, const vec3_t src); -void RotatePointAroundVector(vec3_t dst, const vec3_t dir, - const vec3_t point, float degrees); +void RotatePointAroundVector(vec3_t dst, + const vec3_t dir, + const vec3_t point, + float degrees); /* ============================================= */ -char *COM_SkipPath(char *pathname); -void COM_StripExtension(char *in, char *out); -void COM_FileBase(char *in, char *out); +const char *COM_SkipPath(const char *pathname); +void COM_StripExtension(const char *in, char *out); +YQ2_ATTR_RETURNS_NONNULL const char *COM_FileExtension(const char *in); +void COM_FileBase(const char *in, char *out); void COM_FilePath(const char *in, char *out); void COM_DefaultExtension(char *path, const char *extension); -char *COM_Parse(char **data_p); +YQ2_ATTR_RETURNS_NONNULL const char *COM_Parse(char **data_p); /* data is an in/out parm, returns a parsed out token */ -void Com_sprintf(char *dest, int size, char *fmt, ...); +void Com_sprintf(char *dest, int size, const char *fmt, ...); -void Com_PageInMemory(byte *buffer, int size); +void Com_PageInMemory(const byte *buffer, int size); /* ============================================= */ /* portable case insensitive compare */ int Q_stricmp(const char *s1, const char *s2); -int Q_strcasecmp(char *s1, char *s2); -int Q_strncasecmp(char *s1, char *s2, int n); +int Q_strcasecmp(const char *s1, const char *s2); +int Q_strncasecmp(const char *s1, const char *s2, int n); +char *Q_strcasestr(const char *s1, const char *s2); + +/* portable string lowercase */ +char *Q_strlwr(char *s); + +/* portable safe string copy/concatenate */ int Q_strlcpy(char *dst, const char *src, int size); int Q_strlcat(char *dst, const char *src, int size); +/* Copies only ASCII chars > 31 && < 127 from s to d, up to n - 1 + * Returns space needed to fully copy s to d (minus null char) + * Does not modify d at all if n is 0 + * Example: needed = Q_strlcpy_ascii(d, "b\robby", 3) + * needed is 5 and d contains "bo\0" + */ +size_t Q_strlcpy_ascii(char *d, const char *s, size_t n); + +/* Delete n characters from s starting at index i */ +void Q_strdel(char *s, size_t i, size_t n); + +/* Insert src into dest starting at index i, total, n is the total size of the buffer */ +/* Returns length of src on success, 0 if there is not enough space in dest for src */ +size_t Q_strins(char *dest, const char *src, size_t i, size_t n); +qboolean Q_strisnum(const char *s); + +/* fix backslashes in path */ +void Q_replacebackslash(char *curr); + +/* A strchr that can search for multiple characters + * chrs is a string of characters to search for + * If found, returns a pointer to that char inside s, NULL otherwise + */ +char *Q_strchrs(const char *s, const char *chrs); + +/* Returns a pointer to c in s if found + * Otherwise returns a pointer to the null-terminator at the end of s + */ +char *Q_strchr0(const char *s, char c); + +/* ============================================= */ + +/* Unicode wrappers that also make sure it's a regular file around fopen(). */ +FILE *Q_fopen(const char *file, const char *mode); + +/* Comparator function for qsort(), compares case-insensitive strings. */ +int Q_sort_stricmp(const void *s1, const void *s2); + +/* Comparator function for qsort(), compares strings. */ +int Q_sort_strcomp(const void *s1, const void *s2); + /* ============================================= */ short BigShort(short l); @@ -251,19 +403,28 @@ float BigFloat(float l); float LittleFloat(float l); void Swap_Init(void); -char *va(char *format, ...); +char *va(const char *format, ...) PRINTF_ATTR(1, 2); /* ============================================= */ /* key / value info strings */ #define MAX_INFO_KEY 64 #define MAX_INFO_VALUE 64 +#define MAX_INFO_KEYVAL ((MAX_INFO_KEY - 1) + (MAX_INFO_VALUE - 1) + 3) /* 3 for 2 backslashes and null char */ #define MAX_INFO_STRING 512 -char *Info_ValueForKey(char *s, char *key); -void Info_RemoveKey(char *s, char *key); -void Info_SetValueForKey(char *s, char *key, char *value); -qboolean Info_Validate(char *s); +char *Info_ValueForKey(const char *s, const char *key); +void Info_RemoveKey(char *s, const char *key); +void Info_SetValueForKey(char *s, const char *key, const char *value); +qboolean Info_Validate(const char *s); + +/* ============================================= */ + +/* Random number generator */ +int randk(void); +float frandk(void); +float crandk(void); +void randk_seed(void); /* * ============================================================== @@ -276,13 +437,13 @@ qboolean Info_Validate(char *s); extern int curtime; /* time returned by last Sys_Milliseconds */ int Sys_Milliseconds(void); -void Sys_Mkdir(char *path); -void Sys_Rmdir(char *path); -char *strlwr(char *s); +void Sys_Mkdir(const char *path); +qboolean Sys_IsDir(const char *path); +qboolean Sys_IsFile(const char *path); /* large block stack allocation routines */ -void *Hunk_Begin(int maxsize); -void *Hunk_Alloc(int size); +YQ2_ATTR_MALLOC void *Hunk_Begin(int maxsize); +YQ2_ATTR_MALLOC void *Hunk_Alloc(int size); void Hunk_Free(void *buf); int Hunk_End(void); @@ -294,13 +455,13 @@ int Hunk_End(void); #define SFF_SYSTEM 0x10 /* pass in an attribute mask of things you wish to REJECT */ -char *Sys_FindFirst(char *path, unsigned musthave, unsigned canthave); +char *Sys_FindFirst(const char *path, unsigned musthave, unsigned canthave); char *Sys_FindNext(unsigned musthave, unsigned canthave); void Sys_FindClose(void); -/* this is only here so the functions in q_shared.c and q_shwin.c can link */ -void Sys_Error(char *error, ...); -void Com_Printf(char *msg, ...); +/* this is only here so the functions in shared source files can link */ +YQ2_ATTR_NORETURN void Sys_Error(const char *error, ...); +void Com_Printf(const char *msg, ...); /* * ========================================================== @@ -330,6 +491,9 @@ typedef struct cvar_s qboolean modified; /* set each time the cvar is changed */ float value; struct cvar_s *next; + + /* Added by YQ2. Must be at the end to preserve ABI. */ + char *default_string; } cvar_t; #endif /* CVAR */ @@ -388,19 +552,23 @@ typedef struct cvar_s /* content masks */ #define MASK_ALL (-1) #define MASK_SOLID (CONTENTS_SOLID | CONTENTS_WINDOW) -#define MASK_PLAYERSOLID (CONTENTS_SOLID | CONTENTS_PLAYERCLIP | \ - CONTENTS_WINDOW | CONTENTS_MONSTER) +#define MASK_PLAYERSOLID \ + (CONTENTS_SOLID | CONTENTS_PLAYERCLIP | \ + CONTENTS_WINDOW | CONTENTS_MONSTER) #define MASK_DEADSOLID (CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW) -#define MASK_MONSTERSOLID (CONTENTS_SOLID | CONTENTS_MONSTERCLIP | \ - CONTENTS_WINDOW | CONTENTS_MONSTER) +#define MASK_MONSTERSOLID \ + (CONTENTS_SOLID | CONTENTS_MONSTERCLIP | \ + CONTENTS_WINDOW | CONTENTS_MONSTER) #define MASK_WATER (CONTENTS_WATER | CONTENTS_LAVA | CONTENTS_SLIME) #define MASK_OPAQUE (CONTENTS_SOLID | CONTENTS_SLIME | CONTENTS_LAVA) -#define MASK_SHOT (CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_WINDOW | \ - CONTENTS_DEADMONSTER) -#define MASK_CURRENT (CONTENTS_CURRENT_0 | CONTENTS_CURRENT_90 | \ - CONTENTS_CURRENT_180 | CONTENTS_CURRENT_270 | \ - CONTENTS_CURRENT_UP | \ - CONTENTS_CURRENT_DOWN) +#define MASK_SHOT \ + (CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_WINDOW | \ + CONTENTS_DEADMONSTER) +#define MASK_CURRENT \ + (CONTENTS_CURRENT_0 | CONTENTS_CURRENT_90 | \ + CONTENTS_CURRENT_180 | CONTENTS_CURRENT_270 | \ + CONTENTS_CURRENT_UP | \ + CONTENTS_CURRENT_DOWN) /* gi.BoxEdicts() can return a list of either solid or trigger entities */ #define AREA_SOLID 1 @@ -412,7 +580,7 @@ typedef struct cplane_s vec3_t normal; float dist; byte type; /* for fast side tests */ - byte signbits; /* signx + (signy<<1) + (signz<<1) */ + byte signbits; /* signx + (signy<<1) + (signz<<2) */ byte pad[2]; } cplane_t; @@ -436,8 +604,8 @@ typedef struct cmodel_s typedef struct csurface_s { char name[16]; - int flags; - int value; + int flags; /* SURF_* */ + int value; /* unused */ } csurface_t; typedef struct mapsurface_s /* used internally due to name len probs */ @@ -482,10 +650,10 @@ typedef enum #define PMF_NO_PREDICTION 64 /* temporarily disables prediction (used for grappling hook) */ /* this structure needs to be communicated bit-accurate/ - from the server to the client to guarantee that - prediction stays in sync, so no floats are used. - if any part of the game code modifies this struct, it - will result in a prediction error of some degree. */ + * from the server to the client to guarantee that + * prediction stays in sync, so no floats are used. + * if any part of the game code modifies this struct, it + * will result in a prediction error of some degree. */ typedef struct { pmtype_t pm_type; @@ -495,8 +663,8 @@ typedef struct byte pm_flags; /* ducked, jump_held, etc */ byte pm_time; /* each unit = 8 ms */ short gravity; - short delta_angles[3]; /* add to command angles to get view direction - changed by spawns, rotating objects, and teleporters */ + short delta_angles[3]; /* add to command angles to get view direction + * changed by spawns, rotating objects, and teleporters */ } pmove_state_t; /* button bits */ @@ -543,11 +711,11 @@ typedef struct int (*pointcontents)(vec3_t point); } pmove_t; -/* entity_state_t->effects - Effects are things handled on the client side (lights, particles, - frame animations) that happen constantly on the given entity. - An entity that has effects will be sent to the client even if - it has a zero index model. */ +/* entity_state_t->effects + * Effects are things handled on the client side (lights, particles, + * frame animations) that happen constantly on the given entity. + * An entity that has effects will be sent to the client even if + * it has a zero index model. */ #define EF_ROTATE 0x00000001 /* rotate (bonus items) */ #define EF_GIB 0x00000002 /* leave a trail */ #define EF_BLASTER 0x00000008 /* redlight + trail */ @@ -804,7 +972,7 @@ typedef struct #define MZ2_WIDOW_DISRUPTOR 148 #define MZ2_WIDOW_BLASTER 149 #define MZ2_WIDOW_RAIL 150 -#define MZ2_WIDOW_PLASMABEAM 151 +#define MZ2_WIDOW_PLASMABEAM 151 #define MZ2_CARRIER_MACHINEGUN_L2 152 #define MZ2_CARRIER_MACHINEGUN_R2 153 #define MZ2_WIDOW_RAIL_LEFT 154 @@ -867,10 +1035,10 @@ typedef struct extern vec3_t monster_flash_offset[]; -/* Temp entity events are for things that happen - at a location seperate from any existing entity. - Temporary entity messages are explicitly constructed - and broadcast. */ +/* Temp entity events are for things that happen + * at a location seperate from any existing entity. + * Temporary entity messages are explicitly constructed + * and broadcast. */ typedef enum { TE_GUNSHOT, @@ -940,9 +1108,9 @@ typedef enum #define SPLASH_BLOOD 6 /* sound channels: - channel 0 never willingly overrides - other channels (1-7) allways override - a playing sound on that channel */ + * channel 0 never willingly overrides + * other channels (1-7) allways override + * a playing sound on that channel */ #define CHAN_AUTO 0 #define CHAN_WEAPON 1 #define CHAN_VOICE 2 @@ -1016,15 +1184,17 @@ typedef enum #define ANGLE2SHORT(x) ((int)((x) * 65536 / 360) & 65535) #define SHORT2ANGLE(x) ((x) * (360.0 / 65536)) -/* config strings are a general means of communication from - the server to all connected clients. Each config string - can be at most MAX_QPATH characters. */ +/* config strings are a general means of communication from + * the server to all connected clients. Each config string + * can be at most MAX_QPATH characters. */ #define CS_NAME 0 #define CS_CDTRACK 1 #define CS_SKY 2 #define CS_SKYAXIS 3 /* %f %f %f format */ #define CS_SKYROTATE 4 #define CS_STATUSBAR 5 /* display program string */ +#define CS_STATUSBAR_END 29 +#define CS_STATUSBAR_SPACE(i) ((CS_STATUSBAR_END - (i)) * MAX_QPATH) #define CS_AIRACCEL 29 /* air acceleration control */ #define CS_MAXCLIENTS 30 @@ -1041,10 +1211,10 @@ typedef enum /* ============================================== */ -/* entity_state_t->event values - entity events are for effects that take place reletive - to an existing entities origin. Very network efficient. - All muzzle flashes really should be converted to events... */ +/* entity_state_t->event values + * entity events are for effects that take place reletive + * to an existing entities origin. Very network efficient. + * All muzzle flashes really should be converted to events... */ typedef enum { EV_NONE, @@ -1057,9 +1227,9 @@ typedef enum EV_OTHER_TELEPORT } entity_event_t; -/* entity_state_t is the information conveyed from the server - in an update message about entities that the client will - need to render in some way */ +/* entity_state_t is the information conveyed from the server + * in an update message about entities that the client will + * need to render in some way */ typedef struct entity_state_s { int number; /* edict index */ @@ -1071,7 +1241,7 @@ typedef struct entity_state_s int modelindex2, modelindex3, modelindex4; /* weapons, CTF flags, etc */ int frame; int skinnum; - unsigned int effects; + unsigned int effects; int renderfx; int solid; /* for client side prediction, 8*(bits 0-4) is x/y radius */ /* 8*(bits 5-9) is z down distance, 8(bits10-15) is z up */ @@ -1084,10 +1254,10 @@ typedef struct entity_state_s /* ============================================== */ -/* player_state_t is the information needed in addition to pmove_state_t - to rendered a view. There will only be 10 player_state_t sent each second, - but the number of pmove_state_t changes will be reletive to client - frame rates */ +/* player_state_t is the information needed in addition to pmove_state_t + * to rendered a view. There will only be 10 player_state_t sent each second, + * but the number of pmove_state_t changes will be reletive to client + * frame rates */ typedef struct { pmove_state_t pmove; /* for prediction */ @@ -1109,13 +1279,43 @@ typedef struct short stats[MAX_STATS]; /* fast status bar updates */ } player_state_t; -#define VIDREF_GL 1 -#define VIDREF_SOFT 2 -#define VIDREF_OTHER 3 - -extern int vidref_val; - size_t verify_fread(void *, size_t, size_t, FILE *); size_t verify_fwrite(void *, size_t, size_t, FILE *); +/* Returns the next power of 2 value greater-than-or-equal to i + * Examples: + * NextPow2(733) == 1024 + * NextPow2(2048) == 2048 + * Returns 1 if i == 0 and 0 if i == (1 << 31) + */ +unsigned int NextPow2(unsigned int i); + +/* Returns the next power of 2 value greater than i + * Examples: + * NextPow2gt(733) == 1024 + * NextPow2gt(2048) == 4096 + * Returns 1 if i == 0 and 0 if i == (1 << 31) + */ +unsigned int NextPow2gt(unsigned int i); + +/* Bitlist + * API for using arrays of bits with maximum space efficiency +*/ + +/* number of bits per array index */ +typedef char bitlist_t; +#define BITLIST_BPU (sizeof(bitlist_t) * 8) + +/* calculate length of array given number of bits n + * Example: bitlist_t mybits[BITLIST_SIZE(512)]; +*/ +#define BITLIST_SIZE(n) (1 + (((n) - 1) / BITLIST_BPU)) + +/* Set bit i to 1 or clear it to 0 */ +#define BITLIST_SET(l, i) l[(i) / BITLIST_BPU] |= 1 << ((i) % BITLIST_BPU) +#define BITLIST_CLEAR(l, i) l[(i) / BITLIST_BPU] &= ~(1 << ((i) % BITLIST_BPU)) + +/* test the value of bit i */ +#define BITLIST_ISSET(l, i) (l[(i) / BITLIST_BPU] & (1 << ((i) % BITLIST_BPU))) != 0 + #endif /* CTF_SHARED_H */ diff --git a/src/shared/shared.c b/src/shared/shared.c index 86b0be0..1a60275 100644 --- a/src/shared/shared.c +++ b/src/shared/shared.c @@ -24,6 +24,12 @@ * ======================================================================= */ +#include + +#ifndef _MSC_VER +#include +#endif + #include "../header/shared.h" #define DEG2RAD(a) (a * M_PI) / 180.0F @@ -73,7 +79,7 @@ RotatePointAroundVector(vec3_t dst, const vec3_t dir, im[2][1] = m[1][2]; memset(zrot, 0, sizeof(zrot)); - zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F; + zrot[2][2] = 1.0F; zrot[0][0] = (float)cos(DEG2RAD(degrees)); zrot[0][1] = (float)sin(DEG2RAD(degrees)); @@ -91,7 +97,7 @@ RotatePointAroundVector(vec3_t dst, const vec3_t dir, } void -AngleVectors(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) +AngleVectors(const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) { float angle; static float sr, sp, sy, cr, cp, cy; @@ -129,9 +135,8 @@ AngleVectors(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) } void -AngleVectors2(vec3_t value1, vec3_t angles) +AngleVectors2(const vec3_t value1, vec3_t angles) { - float forward; float yaw, pitch; if ((value1[1] == 0) && (value1[0] == 0)) @@ -150,6 +155,8 @@ AngleVectors2(vec3_t value1, vec3_t angles) } else { + float forward; + if (value1[0]) { yaw = ((float)atan2(value1[1], value1[0]) * 180 / M_PI); @@ -234,7 +241,7 @@ PerpendicularVector(vec3_t dst, const vec3_t src) } void -R_ConcatRotations(float in1[3][3], float in2[3][3], float out[3][3]) +R_ConcatRotations(const float in1[3][3], const float in2[3][3], float out[3][3]) { out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; @@ -257,7 +264,7 @@ R_ConcatRotations(float in1[3][3], float in2[3][3], float out[3][3]) } void -R_ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4]) +R_ConcatTransforms(const float in1[3][4], const float in2[3][4], float out[3][4]) { out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; @@ -323,7 +330,7 @@ anglemod(float a) * This is the slow, general version */ int -BoxOnPlaneSide2(vec3_t emins, vec3_t emaxs, struct cplane_s *p) +BoxOnPlaneSide2(const vec3_t emins, const vec3_t emaxs, const struct cplane_s *p) { int i; float dist1, dist2; @@ -365,7 +372,7 @@ BoxOnPlaneSide2(vec3_t emins, vec3_t emaxs, struct cplane_s *p) * Returns 1, 2, or 1 + 2 */ int -BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, struct cplane_s *p) +BoxOnPlaneSide(const vec3_t emins, const vec3_t emaxs, const struct cplane_s *p) { float dist1, dist2; int sides; @@ -465,13 +472,14 @@ ClearBounds(vec3_t mins, vec3_t maxs) } void -AddPointToBounds(vec3_t v, vec3_t mins, vec3_t maxs) +AddPointToBounds(const vec3_t v, vec3_t mins, vec3_t maxs) { int i; - vec_t val; for (i = 0; i < 3; i++) { + vec_t val; + val = v[i]; if (val < mins[i]) @@ -486,8 +494,36 @@ AddPointToBounds(vec3_t v, vec3_t mins, vec3_t maxs) } } +void +ClosestPointOnBounds(const vec3_t p, const vec3_t amin, const vec3_t amax, vec3_t out) +{ + int i; + + for (i = 0; i < 3; i++) + { + if (amin[i] > p[i]) + { + out[i] = amin[i]; + } + else if (amax[i] < p[i]) + { + out[i] = amax[i]; + } + else + { + out[i] = p[i]; + } + } +} + +qboolean +IsZeroVector(vec3_t v) +{ + return (v[0] == 0.0f && v[1] == 0.0f && v[2] == 0.0f); +} + int -VectorCompare(vec3_t v1, vec3_t v2) +VectorCompare(const vec3_t v1, const vec3_t v2) { if ((v1[0] != v2[0]) || (v1[1] != v2[1]) || (v1[2] != v2[2])) { @@ -500,13 +536,15 @@ VectorCompare(vec3_t v1, vec3_t v2) vec_t VectorNormalize(vec3_t v) { - float length, ilength; + float length; length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; length = (float)sqrt(length); if (length) { + float ilength; + ilength = 1 / length; v[0] *= ilength; v[1] *= ilength; @@ -517,26 +555,15 @@ VectorNormalize(vec3_t v) } vec_t -VectorNormalize2(vec3_t v, vec3_t out) +VectorNormalize2(const vec3_t v, vec3_t out) { - float length, ilength; + VectorCopy(v, out); - length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; - length = (float)sqrt(length); - - if (length) - { - ilength = 1 / length; - out[0] = v[0] * ilength; - out[1] = v[1] * ilength; - out[2] = v[2] * ilength; - } - - return length; + return VectorNormalize(out); } void -VectorMA(vec3_t veca, float scale, vec3_t vecb, vec3_t vecc) +VectorMA(const vec3_t veca, float scale, const vec3_t vecb, vec3_t vecc) { vecc[0] = veca[0] + scale * vecb[0]; vecc[1] = veca[1] + scale * vecb[1]; @@ -544,13 +571,13 @@ VectorMA(vec3_t veca, float scale, vec3_t vecb, vec3_t vecc) } vec_t -_DotProduct(vec3_t v1, vec3_t v2) +_DotProduct(const vec3_t v1, const vec3_t v2) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } void -_VectorSubtract(vec3_t veca, vec3_t vecb, vec3_t out) +_VectorSubtract(const vec3_t veca, const vec3_t vecb, vec3_t out) { out[0] = veca[0] - vecb[0]; out[1] = veca[1] - vecb[1]; @@ -558,7 +585,7 @@ _VectorSubtract(vec3_t veca, vec3_t vecb, vec3_t out) } void -_VectorAdd(vec3_t veca, vec3_t vecb, vec3_t out) +_VectorAdd(const vec3_t veca, const vec3_t vecb, vec3_t out) { out[0] = veca[0] + vecb[0]; out[1] = veca[1] + vecb[1]; @@ -566,7 +593,7 @@ _VectorAdd(vec3_t veca, vec3_t vecb, vec3_t out) } void -_VectorCopy(vec3_t in, vec3_t out) +_VectorCopy(const vec3_t in, vec3_t out) { out[0] = in[0]; out[1] = in[1]; @@ -574,31 +601,27 @@ _VectorCopy(vec3_t in, vec3_t out) } void -CrossProduct(vec3_t v1, vec3_t v2, vec3_t cross) +CrossProduct(const vec3_t v1, const vec3_t v2, vec3_t cross) { cross[0] = v1[1] * v2[2] - v1[2] * v2[1]; cross[1] = v1[2] * v2[0] - v1[0] * v2[2]; cross[2] = v1[0] * v2[1] - v1[1] * v2[0]; } -double sqrt(double x); - vec_t -VectorLength(vec3_t v) +VectorLengthSquared(vec3_t v) { - int i; - float length; - - length = 0; - - for (i = 0; i < 3; i++) - { - length += v[i] * v[i]; - } + return (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); +} - length = (float)sqrt(length); +double sqrt(double x); - return length; +vec_t +VectorLength(const vec3_t v) +{ + return sqrtf((v[0] * v[0]) + + (v[1] * v[1]) + + (v[2] * v[2])); } void @@ -610,13 +633,91 @@ VectorInverse(vec3_t v) } void -VectorScale(vec3_t in, vec_t scale, vec3_t out) +VectorInverse2(const vec3_t v, vec3_t out) +{ + VectorCopy(v, out); + VectorInverse(out); +} + +void +VectorScale(const vec3_t in, vec_t scale, vec3_t out) { out[0] = in[0] * scale; out[1] = in[1] * scale; out[2] = in[2] * scale; } +void +VectorLerp(const vec3_t v1, const vec3_t v2, const vec_t factor, vec3_t out) +{ + VectorSubtract(v2, v1, out); + VectorScale(out, factor, out); + VectorAdd(out, v1, out); +} + +void +VectorToQuat(const vec3_t v, quat_t out) +{ + out[0] = v[0]; + out[1] = v[1]; + out[2] = v[2]; + out[3] = 0.0f; +} + +void +QuatInverse(const quat_t q, quat_t out) +{ + out[0] = -q[0]; + out[1] = -q[1]; + out[2] = -q[2]; + out[3] = q[3]; +} + +void +QuatMultiply(const quat_t q1, const quat_t q2, quat_t out) +{ + out[0] = q1[3] * q2[0] + q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1]; + out[1] = q1[3] * q2[1] - q1[0] * q2[2] + q1[1] * q2[3] + q1[2] * q2[0]; + out[2] = q1[3] * q2[2] + q1[0] * q2[1] - q1[1] * q2[0] + q1[2] * q2[3]; + out[3] = q1[3] * q2[3] - q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2]; +} + +void +QuatAngleAxis(const vec3_t v, float angle, quat_t out) +{ + const vec_t scale = sinf(angle * 0.5f); + vec3_t v_out; + + VectorNormalize2(v, v_out); + VectorScale(v_out, scale, v_out); + + out[0] = v_out[0]; + out[1] = v_out[1]; + out[2] = v_out[2]; + out[3] = cosf(angle * 0.5f); +} + +void +RotateVectorByUnitQuat(vec3_t v, quat_t q_unit) +{ + quat_t q_vec, q_inv, q_out; + + VectorToQuat(v, q_vec); + QuatInverse(q_unit, q_inv); + QuatMultiply(q_unit, q_vec, q_out); + QuatMultiply(q_out, q_inv, q_out); + + v[0] = q_out[0]; + v[1] = q_out[1]; + v[2] = q_out[2]; +} + +float +Q_magnitude(float x, float y) +{ + return sqrtf(x * x + y * y); +} + int Q_log2(int val) { @@ -632,10 +733,10 @@ Q_log2(int val) /* ==================================================================================== */ -char * -COM_SkipPath(char *pathname) +const char * +COM_SkipPath(const char *pathname) { - char *last; + const char *last; last = pathname; @@ -653,7 +754,7 @@ COM_SkipPath(char *pathname) } void -COM_StripExtension(char *in, char *out) +COM_StripExtension(const char *in, char *out) { while (*in && *in != '.') { @@ -677,9 +778,9 @@ COM_FileExtension(const char *in) } void -COM_FileBase(char *in, char *out) +COM_FileBase(const char *in, char *out) { - char *s, *s2; + const char *s, *s2; s = in + strlen(in) - 1; @@ -699,7 +800,7 @@ COM_FileBase(char *in, char *out) else { s--; - strncpy(out, s2 + 1, s - s2); + memcpy(out, s2 + 1, s - s2); out[s - s2] = 0; } } @@ -719,7 +820,7 @@ COM_FilePath(const char *in, char *out) s--; } - strncpy(out, in, s - in); + memcpy(out, in, s - in); out[s - in] = 0; } @@ -759,12 +860,12 @@ qboolean bigendien; /* can't just use function pointers, or dll linkage can mess up when qcommon is included in multiple places */ -short (*_BigShort)(short l); -short (*_LittleShort)(short l); -int (*_BigLong)(int l); -int (*_LittleLong)(int l); -float (*_BigFloat)(float l); -float (*_LittleFloat)(float l); +static short (*_BigShort)(short l); +static short (*_LittleShort)(short l); +static int (*_BigLong)(int l); +static int (*_LittleLong)(int l); +static float (*_BigFloat)(float l); +static float (*_LittleFloat)(float l); short BigShort(short l) @@ -774,8 +875,8 @@ BigShort(short l) short LittleShort(short l) -{return - _LittleShort(l); +{ + return _LittleShort(l); } int @@ -802,7 +903,7 @@ LittleFloat(float l) return _LittleFloat(l); } -short +static short ShortSwap(short l) { byte b1, b2; @@ -813,13 +914,13 @@ ShortSwap(short l) return (b1 << 8) + b2; } -short +static short ShortNoSwap(short l) { return l; } -int +static int LongSwap(int l) { byte b1, b2, b3, b4; @@ -832,13 +933,13 @@ LongSwap(int l) return ((int)b1 << 24) + ((int)b2 << 16) + ((int)b3 << 8) + b4; } -int +static int LongNoSwap(int l) { return l; } -float +static float FloatSwap(float f) { union @@ -855,7 +956,7 @@ FloatSwap(float f) return dat2.f; } -float +static float FloatNoSwap(float f) { return f; @@ -865,9 +966,12 @@ void Swap_Init(void) { byte swaptest[2] = {1, 0}; + short swapTestShort; + YQ2_STATIC_ASSERT(sizeof(short) == 2, "invalid short size"); + memcpy(&swapTestShort, swaptest, 2); /* set the byte swapping variables in a portable manner */ - if (*(short *)swaptest == 1) + if (swapTestShort == 1) { bigendien = false; _BigShort = ShortSwap; @@ -876,6 +980,7 @@ Swap_Init(void) _LittleLong = LongNoSwap; _BigFloat = FloatSwap; _LittleFloat = FloatNoSwap; + Com_Printf("Byte ordering: little endian\n\n"); } else { @@ -886,7 +991,11 @@ Swap_Init(void) _LittleLong = LongSwap; _BigFloat = FloatNoSwap; _LittleFloat = FloatSwap; + Com_Printf("Byte ordering: big endian\n\n"); } + + if (LittleShort(swapTestShort) != 1) + assert("Error in the endian conversion!"); } /* @@ -894,7 +1003,7 @@ Swap_Init(void) * need to have varargs versions of all text functions. */ char * -va(char *format, ...) +va(const char *format, ...) { va_list argptr; static char string[1024]; @@ -911,7 +1020,7 @@ char com_token[MAX_TOKEN_CHARS]; /* * Parse a token out of a string */ -char * +const char * COM_Parse(char **data_p) { int c; @@ -963,9 +1072,7 @@ COM_Parse(char **data_p) if ((c == '\"') || !c) { - com_token[len] = 0; - *data_p = data; - return com_token; + goto done; } if (len < MAX_TOKEN_CHARS) @@ -990,6 +1097,7 @@ COM_Parse(char **data_p) } while (c > 32); +done: if (len == MAX_TOKEN_CHARS) { len = 0; @@ -1001,10 +1109,10 @@ COM_Parse(char **data_p) return com_token; } -int paged_total; +static int paged_total = 0; void -Com_PageInMemory(byte *buffer, int size) +Com_PageInMemory(const byte *buffer, int size) { int i; @@ -1023,18 +1131,24 @@ Com_PageInMemory(byte *buffer, int size) */ int -Q_stricmp(const char* s1, const char* s2) +Q_stricmp(const char *s1, const char *s2) { - return Q_strcasecmp((char*)s1, (char*)s2); +#ifdef _MSC_VER + return stricmp(s1, s2); +#else + return strcasecmp(s1, s2); +#endif } int -Q_strncasecmp(char *s1, char *s2, int n) +Q_strncasecmp(const char *s1, const char *s2, int n) { - int c1, c2; + int c1; do { + int c2; + c1 = *s1++; c2 = *s2++; @@ -1066,12 +1180,69 @@ Q_strncasecmp(char *s1, char *s2, int n) return 0; /* strings are equal */ } +char *Q_strcasestr(const char *haystack, const char *needle) +{ + size_t len = strlen(needle); + + for (; *haystack; haystack++) + { + if (!Q_strncasecmp(haystack, needle, len)) + { + return (char *)haystack; + } + } + return 0; +} + int -Q_strcasecmp(char *s1, char *s2) +Q_strcasecmp(const char *s1, const char *s2) { return Q_strncasecmp(s1, s2, 99999); } +void +Q_replacebackslash(char *curr) +{ + while (*curr) + { + if (*curr == '\\') + { + *curr = '/'; + } + curr++; + } +} + +void +Com_sprintf(char *dest, int size, const char *fmt, ...) +{ + int len; + va_list argptr; + + va_start(argptr, fmt); + len = vsnprintf(dest, size, fmt, argptr); + va_end(argptr); + + if (len >= size) + { + Com_Printf("%s: overflow\n", __func__); + } +} + +char * +Q_strlwr ( char *s ) +{ + char *p = s; + + while ( *s ) + { + *s = tolower( (unsigned char)*s ); + s++; + } + + return ( p ); +} + int Q_strlcpy(char *dst, const char *src, int size) { @@ -1094,6 +1265,44 @@ Q_strlcpy(char *dst, const char *src, int size) return s - src; } +size_t +Q_strlcpy_ascii(char *d, const char *s, size_t n) +{ + size_t ns = 0; + char c; + int dzero = n == 0; + + if (!dzero) + { + n--; + } + + for (; *s != '\0'; s++) + { + c = *s; + c &= 127; + + if ((c >= 32) && (c < 127)) + { + if (n) + { + *d = c; + d++; + n--; + } + + ns++; + } + } + + if (!dzero) + { + *d = '\0'; + } + + return ns; +} + int Q_strlcat(char *dst, const char *src, int size) { @@ -1108,26 +1317,152 @@ Q_strlcat(char *dst, const char *src, int size) return (d - dst) + Q_strlcpy(d, src, size); } - void -Com_sprintf(char *dest, int size, char *fmt, ...) +Q_strdel(char *s, size_t i, size_t n) { - int len; - va_list argptr; - static char bigbuffer[0x10000]; + size_t len; - va_start(argptr, fmt); - len = vsnprintf(bigbuffer, 0x10000, fmt, argptr); - va_end(argptr); + if (!n) + { + return; + } + + len = strlen(s); + + if (i >= len || n > (len - i)) + { + return; + } + + memmove(s + i, s + i + n, len - i); + s[len - n] = '\0'; +} + +size_t +Q_strins(char *dest, const char *src, size_t i, size_t n) +{ + size_t dlen; + size_t slen; + + if (!src || *src == '\0') + { + return 0; + } + + slen = strlen(src); + dlen = strlen(dest); + + if (i > dlen || (dlen + slen + 1) > n) + { + return 0; + } + + memmove(dest + i + slen, dest + i, dlen - i + 1); + memcpy(dest + i, src, slen); + + return slen; +} + +qboolean +Q_strisnum(const char *s) +{ + for (; *s != '\0'; s++) + { + if (!isdigit(*s)) + { + return false; + } + } + + return true; +} - if ((len >= size) || (len == size)) +char * +Q_strchrs(const char *s, const char *chrs) +{ + char *hit; + + for (; *chrs != '\0'; chrs++) + { + hit = strchr(s, *chrs); + if (hit) + { + return hit; + } + } + + return NULL; +} + +char * +Q_strchr0(const char *s, char c) +{ + while (*s != c && *s != '\0') { - Com_Printf("Com_sprintf: overflow\n"); - len = size - 1; + s++; } - bigbuffer[size - 1] = '\0'; - strcpy(dest, bigbuffer); + return (char *)s; +} + +/* + * An unicode compatible fopen() Wrapper for Windows. + */ +#ifdef _WIN32 +#include +#include +#include +#include +FILE *Q_fopen(const char *file, const char *mode) +{ + WCHAR wfile[MAX_OSPATH]; + WCHAR wmode[16]; + + int len = MultiByteToWideChar(CP_UTF8, 0, file, -1, wfile, MAX_OSPATH); + + if (len > 0) + { + if (MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, 16) > 0) + { + // make sure it's a regular file and not a directory or sth, see #394 + struct _stat buf; + int statret = _wstat(wfile, &buf); + if((statret == 0 && (buf.st_mode & _S_IFREG) != 0) || (statret == -1 && errno == ENOENT)) + { + return _wfopen(wfile, wmode); + } + } + } + + return NULL; +} +#else +#include +#include +FILE *Q_fopen(const char *file, const char *mode) +{ + // make sure it's a regular file and not a directory or sth, see #394 + struct stat statbuf; + int statret = stat(file, &statbuf); + // (it's ok if it doesn't exist though, maybe we wanna write/create) + if((statret == -1 && errno != ENOENT) || (statret == 0 && (statbuf.st_mode & S_IFREG) == 0)) + { + return NULL; + } + return fopen(file, mode); +} +#endif + +int +Q_sort_stricmp(const void *s1, const void *s2) +{ + return Q_stricmp(*(char**)s1, *(char**)s2); +} + +int +Q_sort_strcomp(const void *s1, const void *s2) +{ + return strcmp(*(char **)s1, *(char **)s2); } /* @@ -1144,81 +1479,79 @@ Com_sprintf(char *dest, int size, char *fmt, ...) * or an empty string. */ char * -Info_ValueForKey(char *s, char *key) +Info_ValueForKey(const char *s, const char *key) { - char pkey[512]; - static char value[2][512]; /* use two buffers so compares - work without stomping on each other */ - static int valueindex; - char *o; + /* use two buffers so compares + work without stomping on each other + */ + static char value[2][MAX_INFO_VALUE]; + static int valueindex = 0; + + const char *kstart, *vstart; + char *v; + size_t klen, vlen; valueindex ^= 1; + v = value[valueindex]; + *v = '\0'; - if (*s == '\\') - { - s++; - } + klen = strlen(key); - while (1) + while (*s != '\0') { - o = pkey; - - while (*s != '\\') + if (*s == '\\') { - if (!*s) - { - return ""; - } - - *o++ = *s++; + s++; } - *o = 0; - s++; - - o = value[valueindex]; + kstart = s; + s = Q_strchr0(s, '\\'); - while (*s != '\\' && *s) + if (*s == '\0') { - if (!*s) - { - return ""; - } - - *o++ = *s++; + break; } - *o = 0; + vstart = s + 1; + s = Q_strchr0(vstart, '\\'); - if (!strcmp(key, pkey)) + if (!strncmp(kstart, key, klen) && + kstart[klen] == '\\') { - return value[valueindex]; - } + vlen = s - vstart; - if (!*s) - { - return ""; - } + if (vlen > 0) + { + vlen++; /* Q_strlcpy accounts for null char */ - s++; + Q_strlcpy(v, vstart, + (vlen < MAX_INFO_VALUE) ? vlen : MAX_INFO_VALUE); + } + + break; + } } + + return v; } void -Info_RemoveKey(char *s, char *key) +Info_RemoveKey(char *s, const char *key) { - char *start; - char pkey[512]; - char value[512]; - char *o; + size_t klen; - if (strstr(key, "\\")) + if (strchr(key, '\\')) { return; } - while (1) + klen = strlen(key); + + while (*s != '\0') { + const char *kstart; + char *start; + start = s; if (*s == '\\') @@ -1226,43 +1559,20 @@ Info_RemoveKey(char *s, char *key) s++; } - o = pkey; - - while (*s != '\\') - { - if (!*s) - { - return; - } - - *o++ = *s++; - } - - *o = 0; - s++; - - o = value; - - while (*s != '\\' && *s) - { - if (!*s) - { - return; - } - - *o++ = *s++; - } - - *o = 0; + /* key segment */ + kstart = s; + s = Q_strchr0(s, '\\'); - if (!strcmp(key, pkey)) + if (*s != '\0') { - memmove(start, s, strlen(s) + 1); /* remove this part */ - return; + /* value segment */ + s = Q_strchr0(s + 1, '\\'); } - if (!*s) + if (!strncmp(kstart, key, klen) && + (kstart[klen] == '\\' || kstart[klen] == '\0')) { + memmove(start, s, strlen(s) + 1); return; } } @@ -1273,15 +1583,37 @@ Info_RemoveKey(char *s, char *key) * because they can mess up the server's parsing */ qboolean -Info_Validate(char *s) +Info_Validate(const char *s) +{ + return (Q_strchrs(s, "\";") == NULL) ? true : false; +} + +static qboolean +Info_ValidateKeyValue(const char *key, const char *value) { - if (strstr(s, "\"")) + const char *hit; + + hit = Q_strchrs(key, "\"\\;"); + if (hit) { + Com_Printf("Can't use keys with a '%c'\n", *hit); return false; } - if (strstr(s, ";")) + if (value) + { + hit = Q_strchrs(value, "\"\\"); + if (hit) + { + Com_Printf("Can't use values with a '%c'\n", *hit); + return false; + } + } + + if ((strlen(key) > MAX_INFO_KEY - 1) || + (value && (strlen(value) > MAX_INFO_VALUE - 1))) { + Com_Printf("Keys and values must be < %i characters.\n", MAX_INFO_KEY); return false; } @@ -1289,66 +1621,68 @@ Info_Validate(char *s) } void -Info_SetValueForKey(char *s, char *key, char *value) +Info_SetValueForKey(char *s, const char *key, const char *value) { - char newi[MAX_INFO_STRING], *v; - int c; - int maxsize = MAX_INFO_STRING; + char newi[MAX_INFO_KEYVAL]; + size_t slen, needed; + char *dest; - if (strstr(key, "\\") || strstr(value, "\\")) + if (!key) { - Com_Printf("Can't use keys or values with a \\\n"); return; } - if (strstr(key, ";")) + if (!Info_ValidateKeyValue(key, value)) { - Com_Printf("Can't use keys or values with a semicolon\n"); - return; - } - - if (strstr(key, "\"") || strstr(value, "\"")) - { - Com_Printf("Can't use keys or values with a \"\n"); - return; - } - - if ((strlen(key) > MAX_INFO_KEY - 1) || (strlen(value) > MAX_INFO_KEY - 1)) - { - Com_Printf("Keys and values must be < 64 characters.\n"); return; } Info_RemoveKey(s, key); - if (!value || !strlen(value)) + if (!value || *value == '\0') { return; } Com_sprintf(newi, sizeof(newi), "\\%s\\%s", key, value); - if (strlen(newi) + strlen(s) > maxsize) + slen = strlen(s); + dest = s + slen; + + needed = Q_strlcpy_ascii(dest, newi, MAX_INFO_STRING - slen); + if (needed > (MAX_INFO_STRING - slen - 1)) { Com_Printf("Info string length exceeded\n"); - return; + *dest = '\0'; } +} - /* only copy ascii values */ - s += strlen(s); - v = newi; - - while (*v) +unsigned int +NextPow2(unsigned int i) +{ + if (!i) { - c = *v++; - c &= 127; /* strip high bits */ + return 1U; + } - if ((c >= 32) && (c < 127)) - { - *s++ = c; - } + i--; + + if (i & (1U << 31U)) + { + return 0U; } - *s = 0; + i |= i >> 1U; + i |= i >> 2U; + i |= i >> 4U; + i |= i >> 8U; + i |= i >> 16U; + + return i + 1U; } +unsigned int +NextPow2gt(unsigned int i) +{ + return NextPow2(i + 1U); +} From 515c7a0ca74790bae5383b6e8b2331a45131b722 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Thu, 26 Feb 2026 00:35:29 +0200 Subject: [PATCH 2/7] extend github workflows --- .github/dependabot.yml | 11 +++++++ .github/workflows/codeql.yml | 45 +++++++++++++++++++++++++++++ .github/workflows/linux_aarch64.yml | 24 +++++++++++++-- .github/workflows/linux_x86_64.yml | 13 +++++++-- .github/workflows/macos.yml | 7 +++-- .github/workflows/win32.yml | 7 +++-- .github/workflows/win64.yml | 9 ++++-- 7 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6867e71 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..8a8e80a --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,45 @@ +name: "CodeQL" +run-name: CodeQL +on: + push: + branches: + - 'master' + pull_request: + types: + - edited + - opened + - synchronize +concurrency: + # Cancel concurrent workflows for the same PR or commit hash. + group: ${{github.workflow}}-${{github.event_name == 'pull_request' && github.head_ref || github.sha}} + cancel-in-progress: true +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + security-events: write + packages: read + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: c-cpp + build-mode: autobuild + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/linux_aarch64.yml b/.github/workflows/linux_aarch64.yml index ef11725..6ebfad0 100644 --- a/.github/workflows/linux_aarch64.yml +++ b/.github/workflows/linux_aarch64.yml @@ -15,15 +15,23 @@ concurrency: cancel-in-progress: true jobs: build_ubuntu_aarch64: - runs-on: ubuntu-22.04-arm + runs-on: ubuntu-24.04-arm + permissions: + actions: read + contents: read + security-events: write strategy: fail-fast: false matrix: include: - env: ubuntu steps: + - name: Install build dependencies + run: | + sudo apt update + sudo apt install build-essential snapd - name: Check out repository code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Build run: | # Public runners come with 4 CPUs. @@ -38,8 +46,18 @@ jobs: cp LICENSE publish/quake2-ctf-linux_aarch64-${{github.sha}}/misc/docs/LICENSE.txt cp README.md publish/quake2-ctf-linux_aarch64-${{github.sha}}/misc/docs/README.txt - name: Upload testbuild package - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: quake2-ctf-linux_aarch64-${{github.sha}} path: publish/ if-no-files-found: error + - name: Run Cppcheck + run: | + sudo snap install cppcheck --edge + /snap/bin/cppcheck -j 4 --enable=all --output-format=sarif \ + --output-file=cppcheck_results.sarif --inconclusive --library=posix \ + -DYQ2OSTYPE=\"Linux\" src + - name: Upload SARIF to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: cppcheck_results.sarif diff --git a/.github/workflows/linux_x86_64.yml b/.github/workflows/linux_x86_64.yml index bfa53d0..78c341d 100644 --- a/.github/workflows/linux_x86_64.yml +++ b/.github/workflows/linux_x86_64.yml @@ -15,15 +15,22 @@ concurrency: cancel-in-progress: true jobs: build_ubuntu_x86_64: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest + permissions: + actions: read + contents: read strategy: fail-fast: false matrix: include: - env: ubuntu steps: + - name: Install build dependencies + run: | + sudo apt update + sudo apt install build-essential - name: Check out repository code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Build run: | # Public runners come with 4 CPUs. @@ -38,7 +45,7 @@ jobs: cp LICENSE publish/quake2-ctf-linux_x86_64-${{github.sha}}/misc/docs/LICENSE.txt cp README.md publish/quake2-ctf-linux_x86_64-${{github.sha}}/misc/docs/README.txt - name: Upload testbuild package - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: quake2-ctf-linux_x86_64-${{github.sha}} path: publish/ diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 4210fb6..fe46b98 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -16,6 +16,9 @@ concurrency: jobs: build_macos_aarch64: runs-on: macos-latest + permissions: + actions: read + contents: read strategy: fail-fast: false matrix: @@ -27,7 +30,7 @@ jobs: brew update brew install make - name: Check out repository code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Build run: | # Public runners come with 3 CPUs. @@ -42,7 +45,7 @@ jobs: cp LICENSE publish/quake2-ctf-macos-${{github.sha}}/misc/docs/LICENSE.txt cp README.md publish/quake2-ctf-macos-${{github.sha}}/misc/docs/README.txt - name: Upload testbuild package - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: quake2-ctf-macos-${{github.sha}} path: publish/ diff --git a/.github/workflows/win32.yml b/.github/workflows/win32.yml index 19f9f03..62ff4f1 100644 --- a/.github/workflows/win32.yml +++ b/.github/workflows/win32.yml @@ -16,6 +16,9 @@ concurrency: jobs: build_mingw_x86_32: runs-on: windows-latest + permissions: + actions: read + contents: read strategy: fail-fast: false matrix: @@ -32,7 +35,7 @@ jobs: mingw-w64-${{matrix.env}}-gcc mingw-w64-${{matrix.env}}-make - name: Check out repository code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Build shell: msys2 {0} run: | @@ -49,7 +52,7 @@ jobs: cp LICENSE publish/quake2-ctf-win32-${{github.sha}}/misc/docs/LICENSE.txt cp README.md publish/quake2-ctf-win32-${{github.sha}}/misc/docs/README.txt - name: Upload testbuild package - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: quake2-ctf-win32-${{github.sha}} path: publish/ diff --git a/.github/workflows/win64.yml b/.github/workflows/win64.yml index 419a072..bad6c5f 100644 --- a/.github/workflows/win64.yml +++ b/.github/workflows/win64.yml @@ -16,11 +16,16 @@ concurrency: jobs: build_mingw_x86_64: runs-on: windows-latest + permissions: + actions: read + contents: read strategy: fail-fast: false matrix: include: - { sys: mingw64, env: x86_64 } + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: msys2/setup-msys2@v2 with: @@ -32,7 +37,7 @@ jobs: mingw-w64-${{matrix.env}}-gcc mingw-w64-${{matrix.env}}-make - name: Check out repository code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Build shell: msys2 {0} run: | @@ -49,7 +54,7 @@ jobs: cp LICENSE publish/quake2-ctf-win64-${{github.sha}}/misc/docs/LICENSE.txt cp README.md publish/quake2-ctf-win64-${{github.sha}}/misc/docs/README.txt - name: Upload testbuild package - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: quake2-ctf-win64-${{github.sha}} path: publish/ From 4e3863ede437c2515ee45775408bf56d00aa9772 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Thu, 26 Feb 2026 00:49:28 +0200 Subject: [PATCH 3/7] move spawn function to separate file --- src/g_spawn.c | 511 +++++++++------------------ src/savegame/tables/spawnfunc_decs.h | 144 ++++++++ src/savegame/tables/spawnfunc_list.h | 121 +++++++ 3 files changed, 438 insertions(+), 338 deletions(-) create mode 100644 src/savegame/tables/spawnfunc_decs.h create mode 100644 src/savegame/tables/spawnfunc_list.h diff --git a/src/g_spawn.c b/src/g_spawn.c index b67f889..69ef57e 100644 --- a/src/g_spawn.c +++ b/src/g_spawn.c @@ -25,239 +25,38 @@ */ #include "header/local.h" +#include "savegame/tables/spawnfunc_decs.h" typedef struct { - char *name; + const char *name; void (*spawn)(edict_t *ent); } spawn_t; -void SP_item_health(edict_t *self); -void SP_item_health_small(edict_t *self); -void SP_item_health_large(edict_t *self); -void SP_item_health_mega(edict_t *self); - -void SP_info_player_start(edict_t *ent); -void SP_info_player_deathmatch(edict_t *ent); -void SP_info_player_coop(edict_t *ent); -void SP_info_player_intermission(edict_t *ent); - -void SP_func_plat(edict_t *ent); -void SP_func_rotating(edict_t *ent); -void SP_func_button(edict_t *ent); -void SP_func_door(edict_t *ent); -void SP_func_door_secret(edict_t *ent); -void SP_func_door_rotating(edict_t *ent); -void SP_func_water(edict_t *ent); -void SP_func_train(edict_t *ent); -void SP_func_conveyor(edict_t *self); -void SP_func_wall(edict_t *self); -void SP_func_object(edict_t *self); -void SP_func_explosive(edict_t *self); -void SP_func_timer(edict_t *self); -void SP_func_areaportal(edict_t *ent); -void SP_func_clock(edict_t *ent); -void SP_func_killbox(edict_t *ent); - -void SP_trigger_always(edict_t *ent); -void SP_trigger_once(edict_t *ent); -void SP_trigger_multiple(edict_t *ent); -void SP_trigger_relay(edict_t *ent); -void SP_trigger_push(edict_t *ent); -void SP_trigger_hurt(edict_t *ent); -void SP_trigger_key(edict_t *ent); -void SP_trigger_counter(edict_t *ent); -void SP_trigger_elevator(edict_t *ent); -void SP_trigger_gravity(edict_t *ent); -void SP_trigger_monsterjump(edict_t *ent); - -void SP_target_temp_entity(edict_t *ent); -void SP_target_speaker(edict_t *ent); -void SP_target_explosion(edict_t *ent); -void SP_target_changelevel(edict_t *ent); -void SP_target_secret(edict_t *ent); -void SP_target_goal(edict_t *ent); -void SP_target_splash(edict_t *ent); -void SP_target_spawner(edict_t *ent); -void SP_target_blaster(edict_t *ent); -void SP_target_crosslevel_trigger(edict_t *ent); -void SP_target_crosslevel_target(edict_t *ent); -void SP_target_laser(edict_t *self); -void SP_target_help(edict_t *ent); -void SP_target_actor(edict_t *ent); -void SP_target_lightramp(edict_t *self); -void SP_target_earthquake(edict_t *ent); -void SP_target_character(edict_t *ent); -void SP_target_string(edict_t *ent); - -void SP_worldspawn(edict_t *ent); -void SP_viewthing(edict_t *ent); - -void SP_light(edict_t *self); -void SP_light_mine1(edict_t *ent); -void SP_light_mine2(edict_t *ent); -void SP_info_null(edict_t *self); -void SP_info_notnull(edict_t *self); -void SP_path_corner(edict_t *self); -void SP_point_combat(edict_t *self); - -void SP_misc_explobox(edict_t *self); -void SP_misc_banner(edict_t *self); -void SP_misc_satellite_dish(edict_t *self); -void SP_misc_actor(edict_t *self); -void SP_misc_gib_arm(edict_t *self); -void SP_misc_gib_leg(edict_t *self); -void SP_misc_gib_head(edict_t *self); -void SP_misc_insane(edict_t *self); -void SP_misc_deadsoldier(edict_t *self); -void SP_misc_viper(edict_t *self); -void SP_misc_viper_bomb(edict_t *self); -void SP_misc_bigviper(edict_t *self); -void SP_misc_strogg_ship(edict_t *self); -void SP_misc_teleporter(edict_t *self); -void SP_misc_teleporter_dest(edict_t *self); -void SP_misc_blackhole(edict_t *self); -void SP_misc_eastertank(edict_t *self); -void SP_misc_easterchick(edict_t *self); -void SP_misc_easterchick2(edict_t *self); - -void SP_monster_berserk(edict_t *self); -void SP_monster_gladiator(edict_t *self); -void SP_monster_gunner(edict_t *self); -void SP_monster_infantry(edict_t *self); -void SP_monster_soldier_light(edict_t *self); -void SP_monster_soldier(edict_t *self); -void SP_monster_soldier_ss(edict_t *self); -void SP_monster_tank(edict_t *self); -void SP_monster_medic(edict_t *self); -void SP_monster_flipper(edict_t *self); -void SP_monster_chick(edict_t *self); -void SP_monster_parasite(edict_t *self); -void SP_monster_flyer(edict_t *self); -void SP_monster_brain(edict_t *self); -void SP_monster_floater(edict_t *self); -void SP_monster_hover(edict_t *self); -void SP_monster_mutant(edict_t *self); -void SP_monster_supertank(edict_t *self); -void SP_monster_boss2(edict_t *self); -void SP_monster_jorg(edict_t *self); -void SP_monster_boss3_stand(edict_t *self); - -void SP_monster_commander_body(edict_t *self); - -void SP_turret_breach(edict_t *self); -void SP_turret_base(edict_t *self); -void SP_turret_driver(edict_t *self); - -spawn_t spawns[] = { - {"item_health", SP_item_health}, - {"item_health_small", SP_item_health_small}, - {"item_health_large", SP_item_health_large}, - {"item_health_mega", SP_item_health_mega}, - - {"info_player_start", SP_info_player_start}, - {"info_player_deathmatch", SP_info_player_deathmatch}, - {"info_player_coop", SP_info_player_coop}, - {"info_player_intermission", SP_info_player_intermission}, - {"info_player_team1", SP_info_player_team1}, - {"info_player_team2", SP_info_player_team2}, - - {"func_plat", SP_func_plat}, - {"func_button", SP_func_button}, - {"func_door", SP_func_door}, - {"func_door_secret", SP_func_door_secret}, - {"func_door_rotating", SP_func_door_rotating}, - {"func_rotating", SP_func_rotating}, - {"func_train", SP_func_train}, - {"func_water", SP_func_water}, - {"func_conveyor", SP_func_conveyor}, - {"func_areaportal", SP_func_areaportal}, - {"func_clock", SP_func_clock}, - {"func_wall", SP_func_wall}, - {"func_object", SP_func_object}, - {"func_timer", SP_func_timer}, - {"func_explosive", SP_func_explosive}, - {"func_killbox", SP_func_killbox}, - - {"trigger_always", SP_trigger_always}, - {"trigger_once", SP_trigger_once}, - {"trigger_multiple", SP_trigger_multiple}, - {"trigger_relay", SP_trigger_relay}, - {"trigger_push", SP_trigger_push}, - {"trigger_hurt", SP_trigger_hurt}, - {"trigger_key", SP_trigger_key}, - {"trigger_counter", SP_trigger_counter}, - {"trigger_elevator", SP_trigger_elevator}, - {"trigger_gravity", SP_trigger_gravity}, - {"trigger_monsterjump", SP_trigger_monsterjump}, - - {"target_temp_entity", SP_target_temp_entity}, - {"target_speaker", SP_target_speaker}, - {"target_explosion", SP_target_explosion}, - {"target_changelevel", SP_target_changelevel}, - {"target_secret", SP_target_secret}, - {"target_goal", SP_target_goal}, - {"target_splash", SP_target_splash}, - {"target_spawner", SP_target_spawner}, - {"target_blaster", SP_target_blaster}, - {"target_crosslevel_trigger", SP_target_crosslevel_trigger}, - {"target_crosslevel_target", SP_target_crosslevel_target}, - {"target_laser", SP_target_laser}, - {"target_help", SP_target_help}, - {"target_lightramp", SP_target_lightramp}, - {"target_earthquake", SP_target_earthquake}, - {"target_character", SP_target_character}, - {"target_string", SP_target_string}, - - {"worldspawn", SP_worldspawn}, - {"viewthing", SP_viewthing}, - - {"light", SP_light}, - {"light_mine1", SP_light_mine1}, - {"light_mine2", SP_light_mine2}, - {"info_null", SP_info_null}, - {"func_group", SP_info_null}, - {"info_notnull", SP_info_notnull}, - {"path_corner", SP_path_corner}, - {"point_combat", SP_point_combat}, - - {"misc_explobox", SP_misc_explobox}, - {"misc_banner", SP_misc_banner}, - {"misc_ctf_banner", SP_misc_ctf_banner}, - {"misc_ctf_small_banner", SP_misc_ctf_small_banner}, - {"misc_satellite_dish", SP_misc_satellite_dish}, - {"misc_gib_arm", SP_misc_gib_arm}, - {"misc_gib_leg", SP_misc_gib_leg}, - {"misc_gib_head", SP_misc_gib_head}, - {"misc_viper", SP_misc_viper}, - {"misc_viper_bomb", SP_misc_viper_bomb}, - {"misc_bigviper", SP_misc_bigviper}, - {"misc_strogg_ship", SP_misc_strogg_ship}, - {"misc_teleporter", SP_misc_teleporter}, - {"misc_teleporter_dest", SP_misc_teleporter_dest}, - {"trigger_teleport", SP_trigger_teleport}, - {"info_teleport_destination", SP_info_teleport_destination}, - {"misc_blackhole", SP_misc_blackhole}, - {"misc_eastertank", SP_misc_eastertank}, - {"misc_easterchick", SP_misc_easterchick}, - {"misc_easterchick2", SP_misc_easterchick2}, - - {NULL, NULL} +static spawn_t spawns[] = { +#include "savegame/tables/spawnfunc_list.h" }; /* - * Finds the spawn function for the entity and calls it + * Finds the spawn function for + * the entity and calls it */ void ED_CallSpawn(edict_t *ent) { - spawn_t *s; + const spawn_t *s; gitem_t *item; int i; + if (!ent) + { + return; + } + if (!ent->classname) { - gi.dprintf("ED_CallSpawn: NULL classname\n"); + gi.dprintf("%s: NULL classname\n", __func__); + G_FreeEdict(ent); return; } @@ -292,10 +91,15 @@ ED_CallSpawn(edict_t *ent) } char * -ED_NewString(char *string) +ED_NewString(const char *string) { char *newb, *new_p; - int i, l; + size_t i, l; + + if (!string) + { + return NULL; + } l = strlen(string) + 1; @@ -332,13 +136,18 @@ ED_NewString(char *string) * the binary values in an edict */ static void -ED_ParseField(char *key, const char *value, edict_t *ent) +ED_ParseField(const char *key, const char *value, edict_t *ent) { field_t *f; byte *b; float v; vec3_t vec; + if (!ent || !value || !key) + { + return; + } + for (f = fields; f->name; f++) { if (!Q_stricmp(f->name, key)) @@ -365,13 +174,13 @@ ED_ParseField(char *key, const char *value, edict_t *ent) ((float *)(b + f->ofs))[2] = vec[2]; break; case F_INT: - *(int *)(b + f->ofs) = atoi(value); + *(int *)(b + f->ofs) = (int)strtol(value, (char **)NULL, 10); break; case F_FLOAT: - *(float *)(b + f->ofs) = atof(value); + *(float *)(b + f->ofs) = (float)strtod(value, (char **)NULL); break; case F_ANGLEHACK: - v = atof(value); + v = (float)strtod(value, (char **)NULL); ((float *)(b + f->ofs))[0] = 0; ((float *)(b + f->ofs))[1] = v; ((float *)(b + f->ofs))[2] = 0; @@ -386,19 +195,23 @@ ED_ParseField(char *key, const char *value, edict_t *ent) } } - gi.dprintf("%s is not a field\n", key); + gi.dprintf("'%s' is not a field. Value is '%s'\n", key, value); } /* * Parses an edict out of the given string, - * returning the new position ed should be + * returning the new position. ed should be * a properly initialized empty edict. */ -char * +static char * ED_ParseEdict(char *data, edict_t *ent) { qboolean init; - char keyname[256]; + + if (!ent) + { + return NULL; + } init = false; memset(&st, 0, sizeof(st)); @@ -407,6 +220,7 @@ ED_ParseEdict(char *data, edict_t *ent) while (1) { const char *com_token; + char keyname[256]; /* parse key */ com_token = COM_Parse(&data); @@ -419,28 +233,31 @@ ED_ParseEdict(char *data, edict_t *ent) if (!data) { gi.error("ED_ParseEntity: EOF without closing brace"); + break; } - strncpy(keyname, com_token, sizeof(keyname) - 1); + Q_strlcpy(keyname, com_token, sizeof(keyname)); /* parse value */ com_token = COM_Parse(&data); if (!data) { - gi.error("ED_ParseEntity: EOF without closing brace"); + gi.error("%s: EOF without closing brace", __func__); + break; } if (com_token[0] == '}') { - gi.error("ED_ParseEntity: closing brace without data"); + gi.error("%s: closing brace without data", __func__); + break; } init = true; - /* keynames with a leading underscore are used - for utility comments, and are immediately - discarded by quake */ + /* keynames with a leading underscore are + used for utility comments, and are + immediately discarded by quake */ if (keyname[0] == '_') { continue; @@ -463,7 +280,7 @@ ED_ParseEdict(char *data, edict_t *ent) * All but the first will have the FL_TEAMSLAVE flag set. * All but the last will have the teamchain field set to the next one */ -void +static void G_FindTeams(void) { edict_t *e, *e2, *chain; @@ -531,13 +348,19 @@ G_FindTeams(void) * parsing textual entity definitions out of an ent file. */ void -SpawnEntities(char *mapname, char *entities, char *spawnpoint) +SpawnEntities(const char *mapname, char *entities, const char *spawnpoint) { edict_t *ent; int inhibit; + const char *com_token; int i; float skill_level; + if (!mapname || !entities || !spawnpoint) + { + return; + } + skill_level = floor(skill->value); if (skill_level < 0) @@ -562,8 +385,8 @@ SpawnEntities(char *mapname, char *entities, char *spawnpoint) memset(&level, 0, sizeof(level)); memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0])); - strncpy(level.mapname, mapname, sizeof(level.mapname) - 1); - strncpy(game.spawnpoint, spawnpoint, sizeof(game.spawnpoint) - 1); + Q_strlcpy(level.mapname, mapname, sizeof(level.mapname)); + Q_strlcpy(game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)); /* set client fields on player ents */ for (i = 0; i < game.maxclients; i++) @@ -577,8 +400,6 @@ SpawnEntities(char *mapname, char *entities, char *spawnpoint) /* parse ents */ while (1) { - const char *com_token; - /* parse the opening brace */ com_token = COM_Parse(&entities); @@ -589,7 +410,8 @@ SpawnEntities(char *mapname, char *entities, char *spawnpoint) if (com_token[0] != '{') { - gi.error("ED_LoadFromFile: found %s when expecting {", com_token); + gi.error("%s: found %s when expecting {", __func__, com_token); + break; } if (!ent) @@ -605,8 +427,8 @@ SpawnEntities(char *mapname, char *entities, char *spawnpoint) /* yet another map hack */ if (!Q_stricmp(level.mapname, "command") && - !Q_stricmp(ent->classname, - "trigger_once") && !Q_stricmp(ent->model, "*27")) + !Q_stricmp(ent->classname, "trigger_once") && + !Q_stricmp(ent->model, "*27")) { ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; } @@ -641,13 +463,18 @@ SpawnEntities(char *mapname, char *entities, char *spawnpoint) } } - ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | - SPAWNFLAG_NOT_HARD | SPAWNFLAG_NOT_COOP | SPAWNFLAG_NOT_DEATHMATCH); + ent->spawnflags &= + ~(SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | + SPAWNFLAG_NOT_HARD | + SPAWNFLAG_NOT_COOP | SPAWNFLAG_NOT_DEATHMATCH); } ED_CallSpawn(ent); } + /* in case the last entity in the entstring has spawntemp fields */ + memset(&st, 0, sizeof(st)); + gi.dprintf("%i entities inhibited.\n", inhibit); G_FindTeams(); @@ -659,140 +486,145 @@ SpawnEntities(char *mapname, char *entities, char *spawnpoint) /* =================================================================== */ -char *single_statusbar = -"yb -24 " +static char *single_statusbar = + "yb -24 " /* health */ -"xv 0 " -"hnum " -"xv 50 " -"pic 0 " + "xv 0 " + "hnum " + "xv 50 " + "pic 0 " /* ammo */ -"if 2 " -" xv 100 " -" anum " -" xv 150 " -" pic 2 " -"endif " + "if 2 " + " xv 100 " + " anum " + " xv 150 " + " pic 2 " + "endif " /* armor */ -"if 4 " -" xv 200 " -" rnum " -" xv 250 " -" pic 4 " -"endif " + "if 4 " + " xv 200 " + " rnum " + " xv 250 " + " pic 4 " + "endif " /* selected item */ -"if 6 " -" xv 296 " -" pic 6 " -"endif " + "if 6 " + " xv 296 " + " pic 6 " + "endif " -"yb -50 " + "yb -50 " /* picked up item */ -"if 7 " -" xv 0 " -" pic 7 " -" xv 26 " -" yb -42 " -" stat_string 8 " -" yb -50 " -"endif " + "if 7 " + " xv 0 " + " pic 7 " + " xv 26 " + " yb -42 " + " stat_string 8 " + " yb -50 " + "endif " /* timer */ -"if 9 " -" xv 262 " -" num 2 10 " -" xv 296 " -" pic 9 " -"endif " + "if 9 " + " xv 262 " + " num 2 10 " + " xv 296 " + " pic 9 " + "endif " /* help / weapon icon */ -"if 11 " -" xv 148 " -" pic 11 " -"endif " + "if 11 " + " xv 148 " + " pic 11 " + "endif " ; -char *dm_statusbar = -"yb -24 " +static char *dm_statusbar = + "yb -24 " /* health */ -"xv 0 " -"hnum " -"xv 50 " -"pic 0 " + "xv 0 " + "hnum " + "xv 50 " + "pic 0 " /* ammo */ -"if 2 " -" xv 100 " -" anum " -" xv 150 " -" pic 2 " -"endif " + "if 2 " + " xv 100 " + " anum " + " xv 150 " + " pic 2 " + "endif " /* armor */ -"if 4 " -" xv 200 " -" rnum " -" xv 250 " -" pic 4 " -"endif " + "if 4 " + " xv 200 " + " rnum " + " xv 250 " + " pic 4 " + "endif " /* selected item */ -"if 6 " -" xv 296 " -" pic 6 " -"endif " + "if 6 " + " xv 296 " + " pic 6 " + "endif " -"yb -50 " + "yb -50 " /* picked up item */ -"if 7 " -" xv 0 " -" pic 7 " -" xv 26 " -" yb -42 " -" stat_string 8 " -" yb -50 " -"endif " + "if 7 " + " xv 0 " + " pic 7 " + " xv 26 " + " yb -42 " + " stat_string 8 " + " yb -50 " + "endif " /* timer */ -"if 9 " -" xv 246 " -" num 2 10 " -" xv 296 " -" pic 9 " -"endif " + "if 9 " + " xv 246 " + " num 2 10 " + " xv 296 " + " pic 9 " + "endif " /* help / weapon icon */ -"if 11 " -" xv 148 " -" pic 11 " -"endif " + "if 11 " + " xv 148 " + " pic 11 " + "endif " /* frags */ -"xr -50 " -"yt 2 " -"num 3 14" + "xr -50 " + "yt 2 " + "num 3 14" ; /*QUAKED worldspawn (0 0 0) ? * * Only used for the world. - * "sky" environment map name - * "skyaxis" vector axis for rotating sky - * "skyrotate" speed of rotation in degrees/second - * "sounds" music cd track number - * "gravity" 800 is default gravity - * "message" text to print at user logon + * "sky" environment map name + * "skyaxis" vector axis for rotating sky + * "skyrotate" speed of rotation in degrees/second + * "sounds" music cd track number + * "gravity" 800 is default gravity + * "message" text to print at user logon */ void SP_worldspawn(edict_t *ent) { + if (!ent) + { + return; + } + ent->movetype = MOVETYPE_PUSH; ent->solid = SOLID_BSP; ent->inuse = true; /* since the world doesn't use G_Spawn() */ @@ -800,7 +632,8 @@ SP_worldspawn(edict_t *ent) /* --------------- */ - /* reserve some spots for dead player bodies for coop / deathmatch */ + /* reserve some spots for dead + player bodies for coop / deathmatch */ InitBodyQue(); /* set configstrings for items */ @@ -808,7 +641,7 @@ SP_worldspawn(edict_t *ent) if (st.nextmap) { - strcpy(level.nextmap, st.nextmap); + Q_strlcpy(level.nextmap, st.nextmap, sizeof(level.nextmap)); } /* make some data visible to the server */ @@ -833,7 +666,8 @@ SP_worldspawn(edict_t *ent) gi.configstring(CS_SKYROTATE, va("%f", st.skyrotate)); - gi.configstring(CS_SKYAXIS, va("%f %f %f", st.skyaxis[0], st.skyaxis[1], st.skyaxis[2])); + gi.configstring(CS_SKYAXIS, va("%f %f %f", + st.skyaxis[0], st.skyaxis[1], st.skyaxis[2])); gi.configstring(CS_CDTRACK, va("%i", ent->sounds)); @@ -874,7 +708,7 @@ SP_worldspawn(edict_t *ent) gi.cvar_set("sv_gravity", st.gravity); } - snd_fry = gi.soundindex("player/fry.wav"); /* standing in lava / slime */ + snd_fry = gi.soundindex("player/fry.wav"); /* standing in lava / slime */ PrecacheItem(FindItem("Blaster")); @@ -954,7 +788,8 @@ SP_worldspawn(edict_t *ent) gi.modelindex("models/objects/gibs/skull/tris.md2"); gi.modelindex("models/objects/gibs/head2/tris.md2"); - /* Setup light animation tables. 'a' is total darkness, 'z' is doublebright. */ + /* Setup light animation tables. 'a' + is total darkness, 'z' is doublebright. */ /* 0 normal */ gi.configstring(CS_LIGHTS + 0, "m"); diff --git a/src/savegame/tables/spawnfunc_decs.h b/src/savegame/tables/spawnfunc_decs.h new file mode 100644 index 0000000..d06864b --- /dev/null +++ b/src/savegame/tables/spawnfunc_decs.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * Copyright (C) 2011 Yamagi Burmeister + * Copyright (c) ZeniMax Media Inc. + * + * 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. + * + * ======================================================================= + * + * Prototypes for every spawn function in the game.so. + * + * ======================================================================= + */ + +void SP_item_health(edict_t *self); +void SP_item_health_small(edict_t *self); +void SP_item_health_large(edict_t *self); +void SP_item_health_mega(edict_t *self); + +void SP_info_player_start(edict_t *ent); +void SP_info_player_deathmatch(edict_t *ent); +void SP_info_player_coop(edict_t *ent); +void SP_info_player_intermission(edict_t *ent); + +void SP_func_plat(edict_t *ent); +void SP_func_rotating(edict_t *ent); +void SP_func_button(edict_t *ent); +void SP_func_door(edict_t *ent); +void SP_func_door_secret(edict_t *ent); +void SP_func_door_rotating(edict_t *ent); +void SP_func_water(edict_t *ent); +void SP_func_train(edict_t *ent); +void SP_func_conveyor(edict_t *self); +void SP_func_wall(edict_t *self); +void SP_func_object(edict_t *self); +void SP_func_explosive(edict_t *self); +void SP_func_timer(edict_t *self); +void SP_func_areaportal(edict_t *ent); +void SP_func_clock(edict_t *ent); +void SP_func_killbox(edict_t *ent); + +void SP_trigger_always(edict_t *ent); +void SP_trigger_once(edict_t *ent); +void SP_trigger_multiple(edict_t *ent); +void SP_trigger_relay(edict_t *ent); +void SP_trigger_push(edict_t *ent); +void SP_trigger_hurt(edict_t *ent); +void SP_trigger_key(edict_t *ent); +void SP_trigger_counter(edict_t *ent); +void SP_trigger_elevator(edict_t *ent); +void SP_trigger_gravity(edict_t *ent); +void SP_trigger_monsterjump(edict_t *ent); + +void SP_target_temp_entity(edict_t *ent); +void SP_target_speaker(edict_t *ent); +void SP_target_explosion(edict_t *ent); +void SP_target_changelevel(edict_t *ent); +void SP_target_secret(edict_t *ent); +void SP_target_goal(edict_t *ent); +void SP_target_splash(edict_t *ent); +void SP_target_spawner(edict_t *ent); +void SP_target_blaster(edict_t *ent); +void SP_target_crosslevel_trigger(edict_t *ent); +void SP_target_crosslevel_target(edict_t *ent); +void SP_target_laser(edict_t *self); +void SP_target_help(edict_t *ent); +void SP_target_actor(edict_t *ent); +void SP_target_lightramp(edict_t *self); +void SP_target_earthquake(edict_t *ent); +void SP_target_character(edict_t *ent); +void SP_target_string(edict_t *ent); + +void SP_worldspawn(edict_t *ent); +void SP_viewthing(edict_t *ent); + +void SP_light(edict_t *self); +void SP_light_mine1(edict_t *ent); +void SP_light_mine2(edict_t *ent); +void SP_info_null(edict_t *self); +void SP_info_notnull(edict_t *self); +void SP_path_corner(edict_t *self); +void SP_point_combat(edict_t *self); + +void SP_misc_explobox(edict_t *self); +void SP_misc_banner(edict_t *self); +void SP_misc_satellite_dish(edict_t *self); +void SP_misc_actor(edict_t *self); +void SP_misc_gib_arm(edict_t *self); +void SP_misc_gib_leg(edict_t *self); +void SP_misc_gib_head(edict_t *self); +void SP_misc_insane(edict_t *self); +void SP_misc_deadsoldier(edict_t *self); +void SP_misc_viper(edict_t *self); +void SP_misc_viper_bomb(edict_t *self); +void SP_misc_bigviper(edict_t *self); +void SP_misc_strogg_ship(edict_t *self); +void SP_misc_teleporter(edict_t *self); +void SP_misc_teleporter_dest(edict_t *self); +void SP_misc_blackhole(edict_t *self); +void SP_misc_eastertank(edict_t *self); +void SP_misc_easterchick(edict_t *self); +void SP_misc_easterchick2(edict_t *self); + +void SP_monster_berserk(edict_t *self); +void SP_monster_gladiator(edict_t *self); +void SP_monster_gunner(edict_t *self); +void SP_monster_infantry(edict_t *self); +void SP_monster_soldier_light(edict_t *self); +void SP_monster_soldier(edict_t *self); +void SP_monster_soldier_ss(edict_t *self); +void SP_monster_tank(edict_t *self); +void SP_monster_medic(edict_t *self); +void SP_monster_flipper(edict_t *self); +void SP_monster_chick(edict_t *self); +void SP_monster_parasite(edict_t *self); +void SP_monster_flyer(edict_t *self); +void SP_monster_brain(edict_t *self); +void SP_monster_floater(edict_t *self); +void SP_monster_hover(edict_t *self); +void SP_monster_mutant(edict_t *self); +void SP_monster_supertank(edict_t *self); +void SP_monster_boss2(edict_t *self); +void SP_monster_jorg(edict_t *self); +void SP_monster_boss3_stand(edict_t *self); + +void SP_monster_commander_body(edict_t *self); + +void SP_turret_breach(edict_t *self); +void SP_turret_base(edict_t *self); +void SP_turret_driver(edict_t *self); diff --git a/src/savegame/tables/spawnfunc_list.h b/src/savegame/tables/spawnfunc_list.h new file mode 100644 index 0000000..a53d817 --- /dev/null +++ b/src/savegame/tables/spawnfunc_list.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * Copyright (C) 2011 Yamagi Burmeister + * Copyright (c) ZeniMax Media Inc. + * + * 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. + * + * ======================================================================= + * + * Functionpointers to every spawn function in the game.so. + * + * ======================================================================= + */ + +{"item_health", SP_item_health}, +{"item_health_small", SP_item_health_small}, +{"item_health_large", SP_item_health_large}, +{"item_health_mega", SP_item_health_mega}, + +{"info_player_start", SP_info_player_start}, +{"info_player_deathmatch", SP_info_player_deathmatch}, +{"info_player_coop", SP_info_player_coop}, +{"info_player_intermission", SP_info_player_intermission}, +{"info_player_team1", SP_info_player_team1}, +{"info_player_team2", SP_info_player_team2}, + +{"func_plat", SP_func_plat}, +{"func_button", SP_func_button}, +{"func_door", SP_func_door}, +{"func_door_secret", SP_func_door_secret}, +{"func_door_rotating", SP_func_door_rotating}, +{"func_rotating", SP_func_rotating}, +{"func_train", SP_func_train}, +{"func_water", SP_func_water}, +{"func_conveyor", SP_func_conveyor}, +{"func_areaportal", SP_func_areaportal}, +{"func_clock", SP_func_clock}, +{"func_wall", SP_func_wall}, +{"func_object", SP_func_object}, +{"func_timer", SP_func_timer}, +{"func_explosive", SP_func_explosive}, +{"func_killbox", SP_func_killbox}, + +{"trigger_always", SP_trigger_always}, +{"trigger_once", SP_trigger_once}, +{"trigger_multiple", SP_trigger_multiple}, +{"trigger_relay", SP_trigger_relay}, +{"trigger_push", SP_trigger_push}, +{"trigger_hurt", SP_trigger_hurt}, +{"trigger_key", SP_trigger_key}, +{"trigger_counter", SP_trigger_counter}, +{"trigger_elevator", SP_trigger_elevator}, +{"trigger_gravity", SP_trigger_gravity}, +{"trigger_monsterjump", SP_trigger_monsterjump}, + +{"target_temp_entity", SP_target_temp_entity}, +{"target_speaker", SP_target_speaker}, +{"target_explosion", SP_target_explosion}, +{"target_changelevel", SP_target_changelevel}, +{"target_secret", SP_target_secret}, +{"target_goal", SP_target_goal}, +{"target_splash", SP_target_splash}, +{"target_spawner", SP_target_spawner}, +{"target_blaster", SP_target_blaster}, +{"target_crosslevel_trigger", SP_target_crosslevel_trigger}, +{"target_crosslevel_target", SP_target_crosslevel_target}, +{"target_laser", SP_target_laser}, +{"target_help", SP_target_help}, +{"target_lightramp", SP_target_lightramp}, +{"target_earthquake", SP_target_earthquake}, +{"target_character", SP_target_character}, +{"target_string", SP_target_string}, + +{"worldspawn", SP_worldspawn}, +{"viewthing", SP_viewthing}, + +{"light", SP_light}, +{"light_mine1", SP_light_mine1}, +{"light_mine2", SP_light_mine2}, +{"info_null", SP_info_null}, +{"func_group", SP_info_null}, +{"info_notnull", SP_info_notnull}, +{"path_corner", SP_path_corner}, +{"point_combat", SP_point_combat}, + +{"misc_explobox", SP_misc_explobox}, +{"misc_banner", SP_misc_banner}, +{"misc_ctf_banner", SP_misc_ctf_banner}, +{"misc_ctf_small_banner", SP_misc_ctf_small_banner}, +{"misc_satellite_dish", SP_misc_satellite_dish}, +{"misc_gib_arm", SP_misc_gib_arm}, +{"misc_gib_leg", SP_misc_gib_leg}, +{"misc_gib_head", SP_misc_gib_head}, +{"misc_viper", SP_misc_viper}, +{"misc_viper_bomb", SP_misc_viper_bomb}, +{"misc_bigviper", SP_misc_bigviper}, +{"misc_strogg_ship", SP_misc_strogg_ship}, +{"misc_teleporter", SP_misc_teleporter}, +{"misc_teleporter_dest", SP_misc_teleporter_dest}, +{"trigger_teleport", SP_trigger_teleport}, +{"info_teleport_destination", SP_info_teleport_destination}, +{"misc_blackhole", SP_misc_blackhole}, +{"misc_eastertank", SP_misc_eastertank}, +{"misc_easterchick", SP_misc_easterchick}, +{"misc_easterchick2", SP_misc_easterchick2}, + +{NULL, NULL} From 49b4cf3a5f343b2b858da9e37f5c06e79e7a0cb1 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Thu, 26 Feb 2026 01:08:40 +0200 Subject: [PATCH 4/7] rename src/g_save.c -> src/savegame/savegame.c --- CMakeLists.txt | 2 +- Makefile | 2 +- src/{g_save.c => savegame/savegame.c} | 198 +++++++++++++++++--------- src/savegame/savegame.h | 56 ++++++++ 4 files changed, 185 insertions(+), 73 deletions(-) rename src/{g_save.c => savegame/savegame.c} (83%) create mode 100644 src/savegame/savegame.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 59c1ce7..a496c52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,7 @@ set(Ctf-Source src/g_misc.c src/g_monster.c src/g_phys.c - src/g_save.c + src/savegame/savegame.c src/g_spawn.c src/g_svcmds.c src/g_target.c diff --git a/Makefile b/Makefile index ef457c6..68ae484 100755 --- a/Makefile +++ b/Makefile @@ -232,7 +232,7 @@ CTF_OBJS_ = \ src/g_misc.o \ src/g_monster.o \ src/g_phys.o \ - src/g_save.o \ + src/savegame/savegame.o \ src/g_spawn.o \ src/g_svcmds.o \ src/g_target.o \ diff --git a/src/g_save.c b/src/savegame/savegame.c similarity index 83% rename from src/g_save.c rename to src/savegame/savegame.c index 0583353..7a4556a 100644 --- a/src/g_save.c +++ b/src/savegame/savegame.c @@ -1,5 +1,7 @@ /* * Copyright (C) 1997-2001 Id Software, Inc. + * Copyright (C) 2011 Knightmare + * Copyright (C) 2011 Yamagi Burmeister * * 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 @@ -20,13 +22,14 @@ * ======================================================================= * * The savegame system. Unused by the CTF game but nevertheless called - * during game initialization. Therefor no new savegame code ist + * during game initialization. Therefor no new savegame code ist * imported. * * ======================================================================= - */ + */ -#include "header/local.h" +#include "../header/local.h" +#include "savegame.h" #ifndef BUILD_DATE #define BUILD_DATE __DATE__ @@ -137,9 +140,9 @@ field_t clientfields[] = { }; /* - * This will be called when the dll is first loaded, which - * only happens when a new game is started or a save game - * is loaded. + * This will be called when the dll is first loaded, + * which only happens when a new game is started or + * a save game is loaded. */ void InitGame(void) @@ -207,6 +210,8 @@ InitGame(void) /* others */ aimfix = gi.cvar("aimfix", "0", CVAR_ARCHIVE); + memset(&game, 0, sizeof(game)); + /* items */ InitItems(); @@ -229,11 +234,17 @@ InitGame(void) /* ========================================================= */ -void +/* + * The following two functions are + * doing the dirty work to write the + * data generated by the functions + * below this block into files. + */ +static void WriteField1(FILE *f, field_t *field, byte *base) { void *p; - int len; + size_t len; int index; p = (void *)(base + field->ofs); @@ -302,14 +313,14 @@ WriteField1(FILE *f, field_t *field, byte *base) break; default: - gi.error("WriteEdict: unknown field type"); + gi.error("%s: unknown field type", __func__); } } -void +static void WriteField2(FILE *f, field_t *field, byte *base) { - int len; + size_t len; void *p; p = (void *)(base + field->ofs); @@ -331,13 +342,27 @@ WriteField2(FILE *f, field_t *field, byte *base) } } -void +/* ========================================================= */ + +/* + * This function does the dirty + * work to read the data from a + * file. The processing of the + * data is done in the functions + * below + */ +static void ReadField(FILE *f, field_t *field, byte *base) { void *p; int len; int index; + if (field->flags & FFL_SPAWNTEMP) + { + return; + } + p = (void *)(base + field->ofs); switch (field->type) @@ -358,22 +383,23 @@ ReadField(FILE *f, field_t *field, byte *base) } else { - *(char **)p = gi.TagMalloc(len, TAG_LEVEL); - fread(*(char **)p, len, 1, f); - } - - break; - case F_GSTRING: - len = *(int *)p; - - if (!len) - { - *(char **)p = NULL; - } - else - { - *(char **)p = gi.TagMalloc(len, TAG_GAME); - fread(*(char **)p, len, 1, f); + char *s; + + s = gi.TagMalloc(len + 1, TAG_LEVEL); + if (!s) + { + gi.error("%s: can't allocate string field", __func__); + return; + } + + if (fread(s, len, 1, f) != 1) + { + gi.error("%s: can't read string field", __func__); + return; + } + + s[len] = 0; + *(char **)p = s; } break; @@ -418,17 +444,16 @@ ReadField(FILE *f, field_t *field, byte *base) break; default: - gi.error("ReadEdict: unknown field type"); + gi.error("%s: unknown field type", __func__); } } /* ========================================================= */ /* - * All pointer variables (except function - * pointers) must be handled specially. + * Write the client struct into a file. */ -void +static void WriteClient(FILE *f, gclient_t *client) { field_t *field; @@ -437,7 +462,7 @@ WriteClient(FILE *f, gclient_t *client) /* all of the ints, floats, and vectors stay as they are */ temp = *client; - /* change the pointers to lengths or indexes */ + /* change the pointers to indexes */ for (field = clientfields; field->name; field++) { WriteField1(f, field, (byte *)&temp); @@ -454,15 +479,19 @@ WriteClient(FILE *f, gclient_t *client) } /* - * All pointer variables (except function - * pointers) must be handled specially. + * Read the client struct from a file */ -void +static void ReadClient(FILE *f, gclient_t *client) { field_t *field; - fread(client, sizeof(*client), 1, f); + if (fread(client, sizeof(*client), 1, f) != 1) + { + fclose(f); + gi.error("%s: can't read client", __func__); + return; + } for (field = clientfields; field->name; field++) { @@ -470,18 +499,20 @@ ReadClient(FILE *f, gclient_t *client) } } +/* ========================================================= */ + /* - * This will be called whenever the game goes to a new level, - * and when the user explicitly saves the game. - * - * Game information include cross level data, like multi level - * triggers, help computer info, and all client states. - * - * A single player death will automatically restore from the - * last save position. + * Writes the game struct into + * a file. This is called whenever + * the game goes to a new level or + * the user saves the game. The saved + * information consists of: + * - cross level data + * - client states + * - help computer info */ void -WriteGame(char *filename, qboolean autosave) +WriteGame(const char *filename, qboolean autosave) { FILE *f; int i; @@ -492,11 +523,12 @@ WriteGame(char *filename, qboolean autosave) SaveClientData(); } - f = fopen(filename, "wb"); + f = Q_fopen(filename, "wb"); if (!f) { - gi.error("Couldn't open %s", filename); + gi.error("%s: Couldn't open %s", __func__, filename); + return; } memset(str, 0, sizeof(str)); @@ -515,8 +547,13 @@ WriteGame(char *filename, qboolean autosave) fclose(f); } +/* + * Read the game structs from + * a file. Called when ever a + * savegames is loaded. + */ void -ReadGame(char *filename) +ReadGame(const char *filename) { FILE *f; int i; @@ -524,19 +561,26 @@ ReadGame(char *filename) gi.FreeTags(TAG_GAME); - f = fopen(filename, "rb"); + f = Q_fopen(filename, "rb"); if (!f) { - gi.error("Couldn't open %s", filename); + gi.error("%s: Couldn't open %s", __func__, filename); + return; } - fread(str, sizeof(str), 1, f); + if (fread(str, sizeof(str), 1, f) != 1) + { + fclose(f); + gi.error("%s: can't read save file", __func__); + return; + } if (strcmp(str, BUILD_DATE)) { fclose(f); - gi.error("Savegame from an older version.\n"); + gi.error("Savegame from an incompatible version.\n"); + return; } g_edicts = gi.TagMalloc(game.maxentities * sizeof(g_edicts[0]), TAG_GAME); @@ -556,10 +600,11 @@ ReadGame(char *filename) /* ========================================================== */ /* - * All pointer variables (except function - * pointers) must be handled specially. + * Helper function to write the + * edict into a file. Called by + * WriteLevel. */ -void +static void WriteEdict(FILE *f, edict_t *ent) { field_t *field; @@ -585,10 +630,11 @@ WriteEdict(FILE *f, edict_t *ent) } /* - * All pointer variables (except function - * pointers) must be handled specially. + * Helper function to write the + * level local data into a file. + * Called by WriteLevel. */ -void +static void WriteLevelLocals(FILE *f) { field_t *field; @@ -614,8 +660,8 @@ WriteLevelLocals(FILE *f) } /* - * All pointer variables (except function - * pointers) must be handled specially. + * Writes the current level + * into a file. */ void ReadEdict(FILE *f, edict_t *ent) @@ -648,18 +694,19 @@ ReadLevelLocals(FILE *f) } void -WriteLevel(char *filename) +WriteLevel(const char *filename) { int i; edict_t *ent; FILE *f; void *base; - f = fopen(filename, "wb"); + f = Q_fopen(filename, "wb"); if (!f) { - gi.error("Couldn't open %s", filename); + gi.error("%s: Couldn't open %s", __func__, filename); + return; } /* write out edict size for checking */ @@ -693,6 +740,8 @@ WriteLevel(char *filename) fclose(f); } +/* ========================================================== */ + /* * SpawnEntities will allready have been called on the * level the same way it was when the level was saved. @@ -706,7 +755,7 @@ WriteLevel(char *filename) * No clients are connected yet. */ void -ReadLevel(char *filename) +ReadLevel(const char *filename) { int entnum; FILE *f; @@ -714,11 +763,12 @@ ReadLevel(char *filename) void *base; edict_t *ent; - f = fopen(filename, "rb"); + f = Q_fopen(filename, "rb"); if (!f) { - gi.error("Couldn't open %s", filename); + gi.error("%s: Couldn't open %s", __func__, filename); + return; } /* free any dynamic memory allocated by @@ -730,12 +780,18 @@ ReadLevel(char *filename) globals.num_edicts = maxclients->value + 1; /* check edict size */ - fread(&i, sizeof(i), 1, f); + if (fread(&i, sizeof(i), 1, f) != 1) + { + fclose(f); + gi.error("%s: can't read edict size", __func__); + return; + } if (i != sizeof(edict_t)) { fclose(f); - gi.error("ReadLevel: mismatched edict size"); + gi.error("%s: mismatched edict size", __func__); + return; } /* check function pointer base address */ @@ -756,7 +812,8 @@ ReadLevel(char *filename) if (fread(&entnum, sizeof(entnum), 1, f) != 1) { fclose(f); - gi.error("ReadLevel: failed to read entnum"); + gi.error("%s: failed to read entnum", __func__); + break; } if (entnum == -1) @@ -807,4 +864,3 @@ ReadLevel(char *filename) } } } - diff --git a/src/savegame/savegame.h b/src/savegame/savegame.h new file mode 100644 index 0000000..3f210ac --- /dev/null +++ b/src/savegame/savegame.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * Copyright (C) 2011 Knightmare + * Copyright (C) 2011 Yamagi Burmeister + * + * 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. + * + */ +#ifndef SAVEGAME_LOCAL_H +#define SAVEGAME_LOCAL_H + +/* + * Connects a human readable + * function signature with + * the corresponding pointer + */ +typedef struct +{ + char *funcStr; + byte *funcPtr; +} functionList_t; + +/* + * Connects a human readable + * mmove_t string with the + * corresponding pointer + * */ +typedef struct +{ + char *mmoveStr; + mmove_t *mmovePtr; +} mmoveList_t; + +typedef struct +{ + char ver[32]; + char game[32]; + char os[32]; + char arch[32]; +} savegameHeader_t; + +#endif /* SAVEGAME_LOCAL_H */ From c885055cc46baa59c78be797f81b1669b7378d93 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Thu, 26 Feb 2026 01:17:48 +0200 Subject: [PATCH 5/7] savegame: move fields to src/savegame/tables/ --- src/savegame/savegame.c | 77 +++++------------------------- src/savegame/tables/clientfields.h | 13 +++++ src/savegame/tables/fields.h | 58 ++++++++++++++++++++++ src/savegame/tables/levelfields.h | 16 +++++++ 4 files changed, 100 insertions(+), 64 deletions(-) create mode 100644 src/savegame/tables/clientfields.h create mode 100644 src/savegame/tables/fields.h create mode 100644 src/savegame/tables/levelfields.h diff --git a/src/savegame/savegame.c b/src/savegame/savegame.c index 7a4556a..1d740c1 100644 --- a/src/savegame/savegame.c +++ b/src/savegame/savegame.c @@ -36,55 +36,7 @@ #endif field_t fields[] = { - {"classname", FOFS(classname), F_LSTRING}, - {"origin", FOFS(s.origin), F_VECTOR}, - {"model", FOFS(model), F_LSTRING}, - {"spawnflags", FOFS(spawnflags), F_INT}, - {"speed", FOFS(speed), F_FLOAT}, - {"accel", FOFS(accel), F_FLOAT}, - {"decel", FOFS(decel), F_FLOAT}, - {"target", FOFS(target), F_LSTRING}, - {"targetname", FOFS(targetname), F_LSTRING}, - {"pathtarget", FOFS(pathtarget), F_LSTRING}, - {"deathtarget", FOFS(deathtarget), F_LSTRING}, - {"killtarget", FOFS(killtarget), F_LSTRING}, - {"combattarget", FOFS(combattarget), F_LSTRING}, - {"message", FOFS(message), F_LSTRING}, - {"team", FOFS(team), F_LSTRING}, - {"wait", FOFS(wait), F_FLOAT}, - {"delay", FOFS(delay), F_FLOAT}, - {"random", FOFS(random), F_FLOAT}, - {"move_origin", FOFS(move_origin), F_VECTOR}, - {"move_angles", FOFS(move_angles), F_VECTOR}, - {"style", FOFS(style), F_INT}, - {"count", FOFS(count), F_INT}, - {"health", FOFS(health), F_INT}, - {"sounds", FOFS(sounds), F_INT}, - {"light", 0, F_IGNORE}, - {"dmg", FOFS(dmg), F_INT}, - {"angles", FOFS(s.angles), F_VECTOR}, - {"angle", FOFS(s.angles), F_ANGLEHACK}, - {"mass", FOFS(mass), F_INT}, - {"volume", FOFS(volume), F_FLOAT}, - {"attenuation", FOFS(attenuation), F_FLOAT}, - {"map", FOFS(map), F_LSTRING}, - - /* temp spawn vars -- only valid when the spawn function is called */ - {"lip", STOFS(lip), F_INT, FFL_SPAWNTEMP}, - {"distance", STOFS(distance), F_INT, FFL_SPAWNTEMP}, - {"height", STOFS(height), F_INT, FFL_SPAWNTEMP}, - {"noise", STOFS(noise), F_LSTRING, FFL_SPAWNTEMP}, - {"pausetime", STOFS(pausetime), F_FLOAT, FFL_SPAWNTEMP}, - {"item", STOFS(item), F_LSTRING, FFL_SPAWNTEMP}, - {"gravity", STOFS(gravity), F_LSTRING, FFL_SPAWNTEMP}, - {"sky", STOFS(sky), F_LSTRING, FFL_SPAWNTEMP}, - {"skyrotate", STOFS(skyrotate), F_FLOAT, FFL_SPAWNTEMP}, - {"skyaxis", STOFS(skyaxis), F_VECTOR, FFL_SPAWNTEMP}, - {"minyaw", STOFS(minyaw), F_FLOAT, FFL_SPAWNTEMP}, - {"maxyaw", STOFS(maxyaw), F_FLOAT, FFL_SPAWNTEMP}, - {"minpitch", STOFS(minpitch), F_FLOAT, FFL_SPAWNTEMP}, - {"maxpitch", STOFS(maxpitch), F_FLOAT, FFL_SPAWNTEMP}, - {"nextmap", STOFS(nextmap), F_LSTRING, FFL_SPAWNTEMP} + #include "tables/fields.h" }; field_t savefields[] = { @@ -120,23 +72,20 @@ field_t savefields[] = { {NULL, 0, F_INT} }; -field_t levelfields[] = { - {"", LLOFS(changemap), F_LSTRING}, - - {"", LLOFS(sight_client), F_EDICT}, - {"", LLOFS(sight_entity), F_EDICT}, - {"", LLOFS(sound_entity), F_EDICT}, - {"", LLOFS(sound2_entity), F_EDICT}, - - {NULL, 0, F_INT} +/* + * Level fields to + * be saved + */ +static field_t levelfields[] = { + #include "tables/levelfields.h" }; -field_t clientfields[] = { - {"", CLOFS(pers.weapon), F_ITEM}, - {"", CLOFS(pers.lastweapon), F_ITEM}, - {"", CLOFS(newweapon), F_ITEM}, - - {NULL, 0, F_INT} +/* + * Client fields to + * be saved + */ +static field_t clientfields[] = { + #include "tables/clientfields.h" }; /* diff --git a/src/savegame/tables/clientfields.h b/src/savegame/tables/clientfields.h new file mode 100644 index 0000000..f5fdb6d --- /dev/null +++ b/src/savegame/tables/clientfields.h @@ -0,0 +1,13 @@ +/* + * ======================================================================= + * + * Fields of the client to be saved. + * + * ======================================================================= + */ + +{"", CLOFS(pers.weapon), F_ITEM}, +{"", CLOFS(pers.lastweapon), F_ITEM}, +{"", CLOFS(newweapon), F_ITEM}, + +{NULL, 0, F_INT} diff --git a/src/savegame/tables/fields.h b/src/savegame/tables/fields.h new file mode 100644 index 0000000..cf8fc55 --- /dev/null +++ b/src/savegame/tables/fields.h @@ -0,0 +1,58 @@ +/* + * ======================================================================= + * + * Game fields to be saved. + * + * ======================================================================= + */ + +{"classname", FOFS(classname), F_LSTRING}, +{"origin", FOFS(s.origin), F_VECTOR}, +{"model", FOFS(model), F_LSTRING}, +{"spawnflags", FOFS(spawnflags), F_INT}, +{"speed", FOFS(speed), F_FLOAT}, +{"accel", FOFS(accel), F_FLOAT}, +{"decel", FOFS(decel), F_FLOAT}, +{"target", FOFS(target), F_LSTRING}, +{"targetname", FOFS(targetname), F_LSTRING}, +{"pathtarget", FOFS(pathtarget), F_LSTRING}, +{"deathtarget", FOFS(deathtarget), F_LSTRING}, +{"killtarget", FOFS(killtarget), F_LSTRING}, +{"combattarget", FOFS(combattarget), F_LSTRING}, +{"message", FOFS(message), F_LSTRING}, +{"team", FOFS(team), F_LSTRING}, +{"wait", FOFS(wait), F_FLOAT}, +{"delay", FOFS(delay), F_FLOAT}, +{"random", FOFS(random), F_FLOAT}, +{"move_origin", FOFS(move_origin), F_VECTOR}, +{"move_angles", FOFS(move_angles), F_VECTOR}, +{"style", FOFS(style), F_INT}, +{"count", FOFS(count), F_INT}, +{"health", FOFS(health), F_INT}, +{"sounds", FOFS(sounds), F_INT}, +{"light", 0, F_IGNORE}, +{"dmg", FOFS(dmg), F_INT}, +{"angles", FOFS(s.angles), F_VECTOR}, +{"angle", FOFS(s.angles), F_ANGLEHACK}, +{"mass", FOFS(mass), F_INT}, +{"volume", FOFS(volume), F_FLOAT}, +{"attenuation", FOFS(attenuation), F_FLOAT}, +{"map", FOFS(map), F_LSTRING}, + +/* temp spawn vars -- only valid when the spawn function is called */ +{"lip", STOFS(lip), F_INT, FFL_SPAWNTEMP}, +{"distance", STOFS(distance), F_INT, FFL_SPAWNTEMP}, +{"height", STOFS(height), F_INT, FFL_SPAWNTEMP}, +{"noise", STOFS(noise), F_LSTRING, FFL_SPAWNTEMP}, +{"pausetime", STOFS(pausetime), F_FLOAT, FFL_SPAWNTEMP}, +{"item", STOFS(item), F_LSTRING, FFL_SPAWNTEMP}, +{"gravity", STOFS(gravity), F_LSTRING, FFL_SPAWNTEMP}, +{"sky", STOFS(sky), F_LSTRING, FFL_SPAWNTEMP}, +{"skyrotate", STOFS(skyrotate), F_FLOAT, FFL_SPAWNTEMP}, +{"skyaxis", STOFS(skyaxis), F_VECTOR, FFL_SPAWNTEMP}, +{"minyaw", STOFS(minyaw), F_FLOAT, FFL_SPAWNTEMP}, +{"maxyaw", STOFS(maxyaw), F_FLOAT, FFL_SPAWNTEMP}, +{"minpitch", STOFS(minpitch), F_FLOAT, FFL_SPAWNTEMP}, +{"maxpitch", STOFS(maxpitch), F_FLOAT, FFL_SPAWNTEMP}, +{"nextmap", STOFS(nextmap), F_LSTRING, FFL_SPAWNTEMP}, +{0, 0, 0, 0} diff --git a/src/savegame/tables/levelfields.h b/src/savegame/tables/levelfields.h new file mode 100644 index 0000000..8680f28 --- /dev/null +++ b/src/savegame/tables/levelfields.h @@ -0,0 +1,16 @@ +/* + * ======================================================================= + * + * Fields inside a level to be saved. + * + * ======================================================================= + */ + +{"", LLOFS(changemap), F_LSTRING}, + +{"", LLOFS(sight_client), F_EDICT}, +{"", LLOFS(sight_entity), F_EDICT}, +{"", LLOFS(sound_entity), F_EDICT}, +{"", LLOFS(sound2_entity), F_EDICT}, + +{NULL, 0, F_INT} From a7fbd5854fbe4df2f750ebb8a026fe2c9e8925ea Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Thu, 26 Feb 2026 22:50:25 +0200 Subject: [PATCH 6/7] remove savegame logic https://github.com/yquake2/yquake2/pull/1289 --- src/savegame/savegame.c | 637 +---------------------------- src/savegame/tables/clientfields.h | 13 - src/savegame/tables/levelfields.h | 16 - 3 files changed, 8 insertions(+), 658 deletions(-) delete mode 100644 src/savegame/tables/clientfields.h delete mode 100644 src/savegame/tables/levelfields.h diff --git a/src/savegame/savegame.c b/src/savegame/savegame.c index 1d740c1..c7650d5 100644 --- a/src/savegame/savegame.c +++ b/src/savegame/savegame.c @@ -39,55 +39,6 @@ field_t fields[] = { #include "tables/fields.h" }; -field_t savefields[] = { - {"", FOFS(classname), F_LSTRING}, - {"", FOFS(target), F_LSTRING}, - {"", FOFS(targetname), F_LSTRING}, - {"", FOFS(killtarget), F_LSTRING}, - {"", FOFS(team), F_LSTRING}, - {"", FOFS(pathtarget), F_LSTRING}, - {"", FOFS(deathtarget), F_LSTRING}, - {"", FOFS(combattarget), F_LSTRING}, - {"", FOFS(model), F_LSTRING}, - {"", FOFS(map), F_LSTRING}, - {"", FOFS(message), F_LSTRING}, - - {"", FOFS(client), F_CLIENT}, - {"", FOFS(item), F_ITEM}, - - {"", FOFS(goalentity), F_EDICT}, - {"", FOFS(movetarget), F_EDICT}, - {"", FOFS(enemy), F_EDICT}, - {"", FOFS(oldenemy), F_EDICT}, - {"", FOFS(activator), F_EDICT}, - {"", FOFS(groundentity), F_EDICT}, - {"", FOFS(teamchain), F_EDICT}, - {"", FOFS(teammaster), F_EDICT}, - {"", FOFS(owner), F_EDICT}, - {"", FOFS(mynoise), F_EDICT}, - {"", FOFS(mynoise2), F_EDICT}, - {"", FOFS(target_ent), F_EDICT}, - {"", FOFS(chain), F_EDICT}, - - {NULL, 0, F_INT} -}; - -/* - * Level fields to - * be saved - */ -static field_t levelfields[] = { - #include "tables/levelfields.h" -}; - -/* - * Client fields to - * be saved - */ -static field_t clientfields[] = { - #include "tables/clientfields.h" -}; - /* * This will be called when the dll is first loaded, * which only happens when a new game is started or @@ -183,273 +134,6 @@ InitGame(void) /* ========================================================= */ -/* - * The following two functions are - * doing the dirty work to write the - * data generated by the functions - * below this block into files. - */ -static void -WriteField1(FILE *f, field_t *field, byte *base) -{ - void *p; - size_t len; - int index; - - p = (void *)(base + field->ofs); - - switch (field->type) - { - case F_INT: - case F_FLOAT: - case F_ANGLEHACK: - case F_VECTOR: - case F_IGNORE: - break; - - case F_LSTRING: - case F_GSTRING: - - if (*(char **)p) - { - len = strlen(*(char **)p) + 1; - } - else - { - len = 0; - } - - *(int *)p = len; - break; - case F_EDICT: - - if (*(edict_t **)p == NULL) - { - index = -1; - } - else - { - index = *(edict_t **)p - g_edicts; - } - - *(int *)p = index; - break; - case F_CLIENT: - - if (*(gclient_t **)p == NULL) - { - index = -1; - } - else - { - index = *(gclient_t **)p - game.clients; - } - - *(int *)p = index; - break; - case F_ITEM: - - if (*(edict_t **)p == NULL) - { - index = -1; - } - else - { - index = *(gitem_t **)p - itemlist; - } - - *(int *)p = index; - break; - - default: - gi.error("%s: unknown field type", __func__); - } -} - -static void -WriteField2(FILE *f, field_t *field, byte *base) -{ - size_t len; - void *p; - - p = (void *)(base + field->ofs); - - switch (field->type) - { - case F_LSTRING: - case F_GSTRING: - - if (*(char **)p) - { - len = strlen(*(char **)p) + 1; - fwrite(*(char **)p, len, 1, f); - } - - break; - default: - break; - } -} - -/* ========================================================= */ - -/* - * This function does the dirty - * work to read the data from a - * file. The processing of the - * data is done in the functions - * below - */ -static void -ReadField(FILE *f, field_t *field, byte *base) -{ - void *p; - int len; - int index; - - if (field->flags & FFL_SPAWNTEMP) - { - return; - } - - p = (void *)(base + field->ofs); - - switch (field->type) - { - case F_INT: - case F_FLOAT: - case F_ANGLEHACK: - case F_VECTOR: - case F_IGNORE: - break; - - case F_LSTRING: - len = *(int *)p; - - if (!len) - { - *(char **)p = NULL; - } - else - { - char *s; - - s = gi.TagMalloc(len + 1, TAG_LEVEL); - if (!s) - { - gi.error("%s: can't allocate string field", __func__); - return; - } - - if (fread(s, len, 1, f) != 1) - { - gi.error("%s: can't read string field", __func__); - return; - } - - s[len] = 0; - *(char **)p = s; - } - - break; - case F_EDICT: - index = *(int *)p; - - if (index == -1) - { - *(edict_t **)p = NULL; - } - else - { - *(edict_t **)p = &g_edicts[index]; - } - - break; - case F_CLIENT: - index = *(int *)p; - - if (index == -1) - { - *(gclient_t **)p = NULL; - } - else - { - *(gclient_t **)p = &game.clients[index]; - } - - break; - case F_ITEM: - index = *(int *)p; - - if (index == -1) - { - *(gitem_t **)p = NULL; - } - else - { - *(gitem_t **)p = &itemlist[index]; - } - - break; - - default: - gi.error("%s: unknown field type", __func__); - } -} - -/* ========================================================= */ - -/* - * Write the client struct into a file. - */ -static void -WriteClient(FILE *f, gclient_t *client) -{ - field_t *field; - gclient_t temp; - - /* all of the ints, floats, and vectors stay as they are */ - temp = *client; - - /* change the pointers to indexes */ - for (field = clientfields; field->name; field++) - { - WriteField1(f, field, (byte *)&temp); - } - - /* write the block */ - fwrite(&temp, sizeof(temp), 1, f); - - /* now write any allocated data following the edict */ - for (field = clientfields; field->name; field++) - { - WriteField2(f, field, (byte *)client); - } -} - -/* - * Read the client struct from a file - */ -static void -ReadClient(FILE *f, gclient_t *client) -{ - field_t *field; - - if (fread(client, sizeof(*client), 1, f) != 1) - { - fclose(f); - gi.error("%s: can't read client", __func__); - return; - } - - for (field = clientfields; field->name; field++) - { - ReadField(f, field, (byte *)client); - } -} - -/* ========================================================= */ - /* * Writes the game struct into * a file. This is called whenever @@ -463,37 +147,8 @@ ReadClient(FILE *f, gclient_t *client) void WriteGame(const char *filename, qboolean autosave) { - FILE *f; - int i; - char str[16]; - - if (!autosave) - { - SaveClientData(); - } - - f = Q_fopen(filename, "wb"); - - if (!f) - { - gi.error("%s: Couldn't open %s", __func__, filename); - return; - } - - memset(str, 0, sizeof(str)); - strcpy(str, BUILD_DATE); - fwrite(str, sizeof(str), 1, f); - - game.autosaved = autosave; - fwrite(&game, sizeof(game), 1, f); - game.autosaved = false; - - for (i = 0; i < game.maxclients; i++) - { - WriteClient(f, &game.clients[i]); - } - - fclose(f); + gi.dprintf("%s(%s) is skipped in multiplayer mode\n", + __func__, filename); } /* @@ -504,189 +159,17 @@ WriteGame(const char *filename, qboolean autosave) void ReadGame(const char *filename) { - FILE *f; - int i; - char str[16]; - - gi.FreeTags(TAG_GAME); - - f = Q_fopen(filename, "rb"); - - if (!f) - { - gi.error("%s: Couldn't open %s", __func__, filename); - return; - } - - if (fread(str, sizeof(str), 1, f) != 1) - { - fclose(f); - gi.error("%s: can't read save file", __func__); - return; - } - - if (strcmp(str, BUILD_DATE)) - { - fclose(f); - gi.error("Savegame from an incompatible version.\n"); - return; - } - - g_edicts = gi.TagMalloc(game.maxentities * sizeof(g_edicts[0]), TAG_GAME); - globals.edicts = g_edicts; - - fread(&game, sizeof(game), 1, f); - game.clients = gi.TagMalloc(game.maxclients * sizeof(game.clients[0]), TAG_GAME); - - for (i = 0; i < game.maxclients; i++) - { - ReadClient(f, &game.clients[i]); - } - - fclose(f); + gi.dprintf("%s(%s) is skipped in multiplayer mode\n", + __func__, filename); } /* ========================================================== */ -/* - * Helper function to write the - * edict into a file. Called by - * WriteLevel. - */ -static void -WriteEdict(FILE *f, edict_t *ent) -{ - field_t *field; - edict_t temp; - - /* all of the ints, floats, and vectors stay as they are */ - temp = *ent; - - /* change the pointers to lengths or indexes */ - for (field = savefields; field->name; field++) - { - WriteField1(f, field, (byte *)&temp); - } - - /* write the block */ - fwrite(&temp, sizeof(temp), 1, f); - - /* now write any allocated data following the edict */ - for (field = savefields; field->name; field++) - { - WriteField2(f, field, (byte *)ent); - } -} - -/* - * Helper function to write the - * level local data into a file. - * Called by WriteLevel. - */ -static void -WriteLevelLocals(FILE *f) -{ - field_t *field; - level_locals_t temp; - - /* all of the ints, floats, and vectors stay as they are */ - temp = level; - - /* change the pointers to lengths or indexes */ - for (field = levelfields; field->name; field++) - { - WriteField1(f, field, (byte *)&temp); - } - - /* write the block */ - fwrite(&temp, sizeof(temp), 1, f); - - /* now write any allocated data following the edict */ - for (field = levelfields; field->name; field++) - { - WriteField2(f, field, (byte *)&level); - } -} - -/* - * Writes the current level - * into a file. - */ -void -ReadEdict(FILE *f, edict_t *ent) -{ - field_t *field; - - fread(ent, sizeof(*ent), 1, f); - - for (field = savefields; field->name; field++) - { - ReadField(f, field, (byte *)ent); - } -} - -/* - * All pointer variables (except function - * pointers) must be handled specially. - */ -void -ReadLevelLocals(FILE *f) -{ - field_t *field; - - fread(&level, sizeof(level), 1, f); - - for (field = levelfields; field->name; field++) - { - ReadField(f, field, (byte *)&level); - } -} - void WriteLevel(const char *filename) { - int i; - edict_t *ent; - FILE *f; - void *base; - - f = Q_fopen(filename, "wb"); - - if (!f) - { - gi.error("%s: Couldn't open %s", __func__, filename); - return; - } - - /* write out edict size for checking */ - i = sizeof(edict_t); - fwrite(&i, sizeof(i), 1, f); - - /* write out a function pointer for checking */ - base = (void *)InitGame; - fwrite(&base, sizeof(base), 1, f); - - /* write out level_locals_t */ - WriteLevelLocals(f); - - /* write out all the entities */ - for (i = 0; i < globals.num_edicts; i++) - { - ent = &g_edicts[i]; - - if (!ent->inuse) - { - continue; - } - - fwrite(&i, sizeof(i), 1, f); - WriteEdict(f, ent); - } - - i = -1; - fwrite(&i, sizeof(i), 1, f); - - fclose(f); + gi.dprintf("%s(%s) is skipped in multiplayer mode\n", + __func__, filename); } /* ========================================================== */ @@ -706,110 +189,6 @@ WriteLevel(const char *filename) void ReadLevel(const char *filename) { - int entnum; - FILE *f; - int i; - void *base; - edict_t *ent; - - f = Q_fopen(filename, "rb"); - - if (!f) - { - gi.error("%s: Couldn't open %s", __func__, filename); - return; - } - - /* free any dynamic memory allocated by - loading the level base state */ - gi.FreeTags(TAG_LEVEL); - - /* wipe all the entities */ - memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0])); - globals.num_edicts = maxclients->value + 1; - - /* check edict size */ - if (fread(&i, sizeof(i), 1, f) != 1) - { - fclose(f); - gi.error("%s: can't read edict size", __func__); - return; - } - - if (i != sizeof(edict_t)) - { - fclose(f); - gi.error("%s: mismatched edict size", __func__); - return; - } - - /* check function pointer base address */ - fread(&base, sizeof(base), 1, f); - - if (base != (void *)InitGame) - { - fclose(f); - gi.error("ReadLevel: function pointers have moved"); - } - - /* load the level locals */ - ReadLevelLocals(f); - - /* load all the entities */ - while (1) - { - if (fread(&entnum, sizeof(entnum), 1, f) != 1) - { - fclose(f); - gi.error("%s: failed to read entnum", __func__); - break; - } - - if (entnum == -1) - { - break; - } - - if (entnum >= globals.num_edicts) - { - globals.num_edicts = entnum + 1; - } - - ent = &g_edicts[entnum]; - ReadEdict(f, ent); - - /* let the server rebuild world links for this ent */ - memset(&ent->area, 0, sizeof(ent->area)); - gi.linkentity(ent); - } - - fclose(f); - - /* mark all clients as unconnected */ - for (i = 0; i < maxclients->value; i++) - { - ent = &g_edicts[i + 1]; - ent->client = game.clients + i; - ent->client->pers.connected = false; - } - - /* do any load time things at this point */ - for (i = 0; i < globals.num_edicts; i++) - { - ent = &g_edicts[i]; - - if (!ent->inuse) - { - continue; - } - - /* fire any cross-level triggers */ - if (ent->classname) - { - if (strcmp(ent->classname, "target_crosslevel_target") == 0) - { - ent->nextthink = level.time + ent->delay; - } - } - } + gi.dprintf("%s(%s) is skipped in multiplayer mode\n", + __func__, filename); } diff --git a/src/savegame/tables/clientfields.h b/src/savegame/tables/clientfields.h deleted file mode 100644 index f5fdb6d..0000000 --- a/src/savegame/tables/clientfields.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * ======================================================================= - * - * Fields of the client to be saved. - * - * ======================================================================= - */ - -{"", CLOFS(pers.weapon), F_ITEM}, -{"", CLOFS(pers.lastweapon), F_ITEM}, -{"", CLOFS(newweapon), F_ITEM}, - -{NULL, 0, F_INT} diff --git a/src/savegame/tables/levelfields.h b/src/savegame/tables/levelfields.h deleted file mode 100644 index 8680f28..0000000 --- a/src/savegame/tables/levelfields.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * ======================================================================= - * - * Fields inside a level to be saved. - * - * ======================================================================= - */ - -{"", LLOFS(changemap), F_LSTRING}, - -{"", LLOFS(sight_client), F_EDICT}, -{"", LLOFS(sight_entity), F_EDICT}, -{"", LLOFS(sound_entity), F_EDICT}, -{"", LLOFS(sound2_entity), F_EDICT}, - -{NULL, 0, F_INT} From afaca57090a7f5bbac18e2304873647d62571599 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Thu, 26 Feb 2026 22:55:43 +0200 Subject: [PATCH 7/7] cleanup savegame code --- src/savegame/savegame.c | 1 - src/savegame/savegame.h | 56 ----------------------------------------- 2 files changed, 57 deletions(-) delete mode 100644 src/savegame/savegame.h diff --git a/src/savegame/savegame.c b/src/savegame/savegame.c index c7650d5..0a72abb 100644 --- a/src/savegame/savegame.c +++ b/src/savegame/savegame.c @@ -29,7 +29,6 @@ */ #include "../header/local.h" -#include "savegame.h" #ifndef BUILD_DATE #define BUILD_DATE __DATE__ diff --git a/src/savegame/savegame.h b/src/savegame/savegame.h deleted file mode 100644 index 3f210ac..0000000 --- a/src/savegame/savegame.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 1997-2001 Id Software, Inc. - * Copyright (C) 2011 Knightmare - * Copyright (C) 2011 Yamagi Burmeister - * - * 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. - * - */ -#ifndef SAVEGAME_LOCAL_H -#define SAVEGAME_LOCAL_H - -/* - * Connects a human readable - * function signature with - * the corresponding pointer - */ -typedef struct -{ - char *funcStr; - byte *funcPtr; -} functionList_t; - -/* - * Connects a human readable - * mmove_t string with the - * corresponding pointer - * */ -typedef struct -{ - char *mmoveStr; - mmove_t *mmovePtr; -} mmoveList_t; - -typedef struct -{ - char ver[32]; - char game[32]; - char os[32]; - char arch[32]; -} savegameHeader_t; - -#endif /* SAVEGAME_LOCAL_H */