Eukara wants native-code custom menus. He's weird.

Try to clean up the frametime global.
fix some quirks with r_dynamic -1
Added scr_fov_mode cvar to control whether the fov is defined relative to horizontal, vertical, or guessed.
fix a few fteqcc bugs
tab completion for the gamedir command.
fix the savegame_legacy command. it wasn't entirely compatible.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5254 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2018-05-21 13:47:53 +00:00
parent 6ee709e6d7
commit 5e937bcf48
40 changed files with 1157 additions and 279 deletions

View file

@ -324,6 +324,7 @@ ADD_EXECUTABLE(fteqw WIN32
engine/client/m_multi.c
engine/client/m_options.c
engine/client/m_script.c
engine/client/m_native.c
engine/client/m_single.c
engine/client/menu.c
engine/client/p_classic.c

View file

@ -599,6 +599,7 @@ CLIENT_OBJS = \
m_options.o \
m_single.o \
m_script.o \
m_native.o \
m_mp3.o \
roq_read.o \
clq2_cin.o \

157
engine/client/api_menu.h Normal file
View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2015-2018
* Marco Hladik All rights reserved.
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This 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. If not, see <http://www.gnu.org/licenses/>.
*/
#define NATIVEMENU_API_VERSION_MIN 0 //will be updated any time a symbol is renamed.
#define NATIVEMENU_API_VERSION_MAX 0 //bumped for any change.
#ifndef NATIVEMENU_API_VERSION //so you can hold back the reported version in order to work with older engines.
#define NATIVEMENU_API_VERSION NATIVEMENU_API_VERSION_MAX //version reported to the other side.
#endif
struct vfsfile_s;
struct serverinfo_s;
struct searchpathfuncs_s;
enum slist_test_e;
enum hostcachekey_e; //obtained via calls to gethostcacheindexforkey
enum fs_relative;
#ifndef __QUAKEDEF_H__
#ifdef __cplusplus
typedef enum {qfalse, qtrue} qboolean;//false and true are forcivly defined.
#else
typedef enum {false, true} qboolean;
#endif
typedef float vec_t;
typedef vec_t vec2_t[2];
typedef vec_t vec3_t[3];
typedef vec_t vec4_t[4];
#ifdef _MSC_VER
#define QDECL __cdecl
#else
#define QDECL
#endif
#include <stdint.h>
typedef uint64_t qofs_t;
#endif
struct menu_inputevent_args_s
{
enum {
MIE_KEYDOWN = 0,
MIE_KEYUP = 1,
MIE_MOUSEDELTA = 2,
MIE_MOUSEABS = 3,
} eventtype;
unsigned int devid;
union
{
struct
{
unsigned int scancode;
unsigned int charcode;
} key;
struct
{
float delta[2];
float screen[2]; //virtual coords
} mouse;
};
};
typedef struct {
int api_version; //this may be higher than you expect.
int (*checkextension) (const char *ext);
void (*error) (const char *err);
void (*printf) (const char *text, ...);
void (*dprintf) (const char *text, ...);
void (*localcmd) (const char *cmd);
float (*cvar_float) (const char *name);
const char *(*cvar_string) (const char *name); //return value lasts until cvar_set is called, etc, so don't cache.
void (*cvar_set) (const char *name, const char *value);
void (*registercvar) (const char *name, const char *defaultvalue, unsigned int flags, const char *description);
int (*isserver) (void);
int (*getclientstate) (void);
void (*localsound) (const char *sample, int channel, float volume);
// file input / search crap
struct vfsfile_s *(*fopen) (const char *filename, char *modestring, enum fs_relative fsroot); //modestring should be one of rb,r+b,wb,w+b,ab,wbp. Mostly use a root of FS_GAMEONLY for writes, otherwise FS_GAME for reads.
void (*fclose) (struct vfsfile_s *fhandle);
char *(*fgets) (struct vfsfile_s *fhandle, char *out, size_t outsize); //returns output buffer, or NULL
void (*fprintf) (struct vfsfile_s *fhandle, const char *s, ...);
void (*EnumerateFiles) (const char *match, int (QDECL *callback)(const char *fname, qofs_t fsize, time_t mtime, void *ctx, struct searchpathfuncs_s *package), void *ctx);
// Drawing stuff
// int (*iscachedpic) (const char *name);
void *(*precache_pic) (const char *name);
int (*drawgetimagesize) (void *pic, int *x, int *y);
void (*drawquad) (vec2_t position[4], vec2_t texcoords[4], void *pic, vec4_t rgba, unsigned int be_flags);
// void (*drawsubpic) (vec2_t pos, vec2_t sz, const char *pic, vec2_t srcpos, vec2_t srcsz, vec4_t rgba, unsigned int be_flags);
// void (*drawfill) (vec2_t position, vec2_t size, vec4_t rgba, unsigned int be_flags);
// float (*drawcharacter) (vec2_t position, int character, vec2_t scale, vec4_t rgba, unsigned int be_flags);
// float (*drawrawstring) (vec3_t position, char *text, vec3_t scale, vec4_t rgba, unsigned int be_flags);
float (*drawstring) (vec2_t position, const char *text, float height, vec4_t rgba, unsigned int be_flags);
float (*stringwidth) (const char *text, float height);
void (*drawsetcliparea) (float x, float y, float width, float height);
void (*drawresetcliparea) (void);
// Menu specific stuff
qboolean (*setkeydest) (qboolean focused); //returns whether it changed.
int (*getkeydest) (void); //returns 0 if unfocused, -1 if active-but-unfocused, 1 if focused-and-active.
int (*setmousetarget) (const char *cursorname, float hot_x, float hot_y, float scale); //forces absolute mouse coords whenever cursorname isn't NULL
const char *(*keynumtostring) (int keynum, int modifier);
int (*stringtokeynum) (const char *key, int *modifier);
int (*findkeysforcommand) (int bindmap, const char *command, int *out_scancodes, int *out_modifiers, int keycount);
// Server browser stuff
int (*gethostcachevalue) (int type);
char *(*gethostcachestring) (struct serverinfo_s *host, enum hostcachekey_e fld);
float (*gethostcachenumber) (struct serverinfo_s *host, enum hostcachekey_e fld);
void (*resethostcachemasks) (void);
void (*sethostcachemaskstring) (qboolean or, enum hostcachekey_e fld, char *str, enum slist_test_e op);
void (*sethostcachemasknumber) (qboolean or, enum hostcachekey_e fld, int num, enum slist_test_e op);
void (*sethostcachesort) (enum hostcachekey_e fld, qboolean descending);
void (*resorthostcache) (void);
struct serverinfo_s *(*getsortedhost) (int idx);
void (*refreshhostcache) (qboolean fullreset);
enum hostcachekey_e (*gethostcacheindexforkey) (const char *key);
} menu_import_t;
typedef struct {
int api_version;
void (*Init) (void);
void (*Shutdown) (void);
void (*Draw) (int width, int height, float frametime);
void (*DrawLoading) (int width, int height, float frametime);
void (*Toggle) (int wantmode);
int (*InputEvent) (struct menu_inputevent_args_s ev);
void (*ConsoleCommand) (const char *cmd);
} menu_export_t;
#ifndef NATIVEEXPORT
#ifdef _WIN32
#define NATIVEEXPORTPROTO QDECL
#define NATIVEEXPORT __declspec(dllexport) NATIVEEXPORTPROTO
#else
#define NATIVEEXPORTPROTO
#define NATIVEEXPORT __attribute__((visibility("default")))
#endif
#endif
menu_export_t *NATIVEEXPORTPROTO GetMenuAPI (menu_import_t *import);

View file

@ -4475,7 +4475,7 @@ Host_EndGame
Call this to drop to a console without exiting the qwcl
================
*/
NORETURN void VARGS Host_EndGame (char *message, ...)
NORETURN void VARGS Host_EndGame (const char *message, ...)
{
va_list argptr;
char string[1024];
@ -4510,7 +4510,7 @@ Host_Error
This shuts down the client and exits qwcl
================
*/
void VARGS Host_Error (char *error, ...)
void VARGS Host_Error (const char *error, ...)
{
va_list argptr;
char string[1024];
@ -5581,6 +5581,7 @@ double Host_Frame (double time)
#endif
cls.framecount++;
cl.lasttime = cl.time;
RSpeedRemark();
@ -5799,9 +5800,7 @@ void CL_StartCinematicOrMenu(void)
UI_Start();
#endif
#ifdef MENU_DAT
Cbuf_AddText("menu_restart\n", RESTRICT_LOCAL);
#endif
Con_TPrintf ("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081 %s %sInitialized ^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\n", *fs_gamename.string?fs_gamename.string:"Nothing", com_installer?"Installer ":"");

View file

@ -47,7 +47,7 @@ enum mastertype_e
};
typedef enum
typedef enum hostcachekey_e
{
SLKEY_PING,
SLKEY_MAP,
@ -81,7 +81,7 @@ typedef enum
SLKEY_CUSTOM = SLKEY_PLAYER0+MAX_CLIENTS
} hostcachekey_t;
typedef enum
typedef enum slist_test_e
{
SLIST_TEST_CONTAINS,
SLIST_TEST_NOTCONTAIN,
@ -226,11 +226,11 @@ void MasterInfo_Refresh(qboolean doreset);
void Master_QueryServer(serverinfo_t *server);
void MasterInfo_WriteServers(void);
int Master_KeyForName(const char *keyname);
float Master_ReadKeyFloat(serverinfo_t *server, int keynum);
char *Master_ReadKeyString(serverinfo_t *server, int keynum);
hostcachekey_t Master_KeyForName(const char *keyname);
float Master_ReadKeyFloat(serverinfo_t *server, hostcachekey_t keynum);
char *Master_ReadKeyString(serverinfo_t *server, hostcachekey_t keynum);
void Master_SortServers(void);
int Master_SortServers(void);
void Master_SetSortField(hostcachekey_t field, qboolean descending);
hostcachekey_t Master_GetSortField(void);
qboolean Master_GetSortDescending(void);

View file

@ -955,6 +955,10 @@ void SCR_DrawCursor(void)
cmod = kc_console;
else if ((key_dest_mask & key_dest_absolutemouse & kdm_gmenu))
cmod = kc_menu;
#ifdef MENU_NATIVECODE
else if ((key_dest_mask & key_dest_absolutemouse & kdm_nmenu))
cmod = kc_nmenu;
#endif
else// if (key_dest_mask & key_dest_absolutemouse)
cmod = prydoncursornum?kc_console:kc_game;
@ -1892,6 +1896,13 @@ void SCR_DrawLoading (qboolean opaque)
return; //menuqc should have just drawn whatever overlays it wanted.
}
#endif
#ifdef MENU_NATIVECODE
if (mn_entry && mn_entry->DrawLoading)
{
mn_entry->DrawLoading(vid.width, vid.height, host_frametime);
return;
}
#endif
//int mtype = M_GameType(); //unused variable
y = vid.height/2;
@ -3383,6 +3394,10 @@ void SCR_DrawTwoDimensional(int uimenu, qboolean nohud)
#ifdef MENU_DAT
MP_Draw();
#endif
#ifdef MENU_NATIVECODE
if (mn_entry)
mn_entry->Draw(vid.width, vid.height, host_frametime);
#endif
M_Draw (uimenu);

View file

@ -808,6 +808,7 @@ typedef struct
// the client simulates or interpolates movement to get these values
double time; // this is the time value that the client
// is rendering at. always <= realtime
double lasttime; //cl.time from last frame.
float servertime; //current server time, bound between gametime and gametimemark
float mtime; //server time as on the server when we last received a packet. not allowed to decrease.

View file

@ -704,8 +704,25 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum, float frame
{ //many mods assume only a single mouse device.
//when we have multiple active abs devices, we need to avoid sending all of them, because that just confuses everyone. such mods will see only devices that are actually moving, so uni-cursor mods will see only the one that moved most recently.
mouse->updated = false;
if (!runningindepphys)
{
#ifdef MENU_NATIVECODE
if (mn_entry)
{
struct menu_inputevent_args_s ev = {MIE_MOUSEABS, mouse->qdeviceid};
ev.mouse.delta[0] = mx;
ev.mouse.delta[1] = my;
ev.mouse.screen[0] = (mouse->oldpos[0] * vid.width) / vid.pixelwidth;
ev.mouse.screen[1] = (mouse->oldpos[1] * vid.width) / vid.pixelwidth;
if (mn_entry->InputEvent(ev))
{
mx = 0;
my = 0;
}
}
#endif
#ifdef MENU_DAT
if (!runningindepphys && MP_MousePosition(mouse->oldpos[0], mouse->oldpos[1], mouse->qdeviceid))
if (MP_MousePosition(mouse->oldpos[0], mouse->oldpos[1], mouse->qdeviceid))
{
mx = 0;
my = 0;
@ -720,8 +737,24 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum, float frame
#endif
}
}
}
else
{
#ifdef MENU_NATIVECODE
if (mn_entry && Key_Dest_Has(kdm_nmenu) && (mx || my))
{
struct menu_inputevent_args_s ev = {MIE_MOUSEABS, mouse->qdeviceid};
ev.mouse.delta[0] = mx;
ev.mouse.delta[1] = my;
ev.mouse.screen[0] = (mouse->oldpos[0] * vid.width) / vid.pixelwidth;
ev.mouse.screen[1] = (mouse->oldpos[1] * vid.width) / vid.pixelwidth;
if (mn_entry->InputEvent(ev))
{
mx = 0;
my = 0;
}
}
#endif
#ifdef MENU_DAT
if (Key_Dest_Has(kdm_gmenu))
if (mx || my)

View file

@ -2636,6 +2636,15 @@ void Key_Event (unsigned int devid, int key, unsigned int unicode, qboolean down
#ifdef MENU_DAT
if (Key_Dest_Has(kdm_gmenu) && !Key_Dest_Has(kdm_editor|kdm_console|kdm_cwindows))
MP_Keyup (key, unicode, devid);
#endif
#ifdef MENU_NATIVECODE
if (mn_entry)
{
struct menu_inputevent_args_s ev = {MIE_KEYUP, devid};
ev.key.scancode = key;
ev.key.charcode = unicode;
mn_entry->InputEvent(ev);
}
#endif
return;
}
@ -2659,6 +2668,18 @@ void Key_Event (unsigned int devid, int key, unsigned int unicode, qboolean down
#endif
else if (Key_Dest_Has(kdm_emenu))
M_Keydown (key, unicode);
#ifdef MENU_NATIVECODE
else if (Key_Dest_Has(kdm_nmenu))
{
if (mn_entry)
{
struct menu_inputevent_args_s ev = {MIE_KEYDOWN, devid};
ev.key.scancode = key;
ev.key.charcode = unicode;
mn_entry->InputEvent(ev);
}
}
#endif
#ifdef MENU_DAT
else if (Key_Dest_Has(kdm_gmenu))
MP_Keydown (key, unicode, devid);
@ -2709,6 +2730,15 @@ void Key_Event (unsigned int devid, int key, unsigned int unicode, qboolean down
}
if (Key_Dest_Has(kdm_emenu))
M_Keyup (key, unicode);
#ifdef MENU_NATIVECODE
if (Key_Dest_Has(kdm_nmenu) && mn_entry)
{
struct menu_inputevent_args_s ev = {MIE_KEYUP, devid};
ev.key.scancode = key;
ev.key.charcode = unicode;
mn_entry->InputEvent(ev);
}
#endif
#ifdef MENU_DAT
if (Key_Dest_Has(kdm_gmenu))
MP_Keyup (key, unicode, devid);
@ -2803,6 +2833,19 @@ void Key_Event (unsigned int devid, int key, unsigned int unicode, qboolean down
return;
}
}
#ifdef MENU_NATIVECODE
if (Key_Dest_Has(kdm_nmenu))
{
if (mn_entry)
{
struct menu_inputevent_args_s ev = {MIE_KEYDOWN, devid};
ev.key.scancode = key;
ev.key.charcode = unicode;
if (mn_entry->InputEvent(ev))
return;
}
}
#endif
#ifdef MENU_DAT
if (Key_Dest_Has(kdm_gmenu))
{

View file

@ -250,16 +250,22 @@ typedef enum //highest has priority
kdm_centerprint = 1u<<1, //enabled when there's a centerprint menu with clickable things.
kdm_message = 1u<<2,
kdm_gmenu = 1u<<3, //menu.dat
kdm_emenu = 1u<<4, //engine's menus
kdm_editor = 1u<<5,
kdm_console = 1u<<6,
kdm_cwindows = 1u<<7,
#ifdef MENU_NATIVECODE
kdm_nmenu = 1u<<4,
#else
kdm_nmenu = 0,
#endif
kdm_emenu = 1u<<5, //engine's menus
kdm_editor = 1u<<6,
kdm_console = 1u<<7,
kdm_cwindows = 1u<<8,
} keydestmask_t;
//unsigned int Key_Dest_Get(void); //returns highest priority destination
#define Key_Dest_Add(kdm) (key_dest_mask |= (kdm))
#define Key_Dest_Remove(kdm) (key_dest_mask &= ~(kdm))
#define Key_Dest_Has(kdm) (key_dest_mask & (kdm))
#define Key_Dest_Has_Higher(kdm) (key_dest_mask & (~0&~((kdm)|((kdm)-1)))) //must be a single bit
#define Key_Dest_Toggle(kdm) do {if (key_dest_mask & kdm) Key_Dest_Remove(kdm); else Key_Dest_Add(kdm);}while(0)
extern unsigned int key_dest_absolutemouse; //if the active key dest bit is set, the mouse is absolute.
@ -271,9 +277,12 @@ extern int key_lastpress;
enum
{
kc_game,
kc_menu,
kc_console,
kc_game, //csprogs.dat
kc_menu, //menu.dat
#ifdef MENU_NATIVECODE
kc_nmenu,
#endif
kc_console, //generic engine-defined cursor
kc_max
};
extern struct key_cursor_s

286
engine/client/m_native.c Normal file
View file

@ -0,0 +1,286 @@
#include "quakedef.h"
#ifdef MENU_NATIVECODE
static dllhandle_t *libmenu;
menu_export_t *mn_entry;
extern unsigned int r2d_be_flags;
#include "pr_common.h"
#include "shader.h"
#include "cl_master.h"
static int MN_checkextension(const char *extname)
{
unsigned int i;
for (i = 0; i < QSG_Extensions_count; i++)
{
if (!strcmp(QSG_Extensions[i].name, extname))
return true;
}
return false;
}
static void MN_localcmd(const char *text)
{
Cbuf_AddText(text, RESTRICT_LOCAL); //menus are implicitly trusted. latching and other stuff would be a nightmare otherwise.
}
static void MN_registercvar(const char *cvarname, const char *defaulttext, unsigned int flags, const char *description)
{
Cvar_Get2(cvarname, defaulttext, flags, description, NULL);
}
static int MN_getserverstate(void)
{
if (!sv.active)
return 0;
if (svs.allocated_client_slots <= 1)
return 1;
return 2;
}
static int MN_getclientstate(void)
{
if (cls.state >= ca_active)
return 2;
if (cls.state != ca_disconnected)
return 1;
return 0;
}
static void MN_fclose(vfsfile_t *f)
{
VFS_CLOSE(f);
}
static void *MN_precache_pic(const char *picname)
{
return R2D_SafeCachePic(picname);
}
static int MN_drawgetimagesize(void *pic, int *w, int *h)
{
return R_GetShaderSizes(pic, w, h, true);
}
static void MN_drawquad(vec2_t position[4], vec2_t texcoords[4], void *pic, vec4_t rgba, unsigned int be_flags)
{
r2d_be_flags = be_flags;
R2D_ImageColours(rgba[0], rgba[1], rgba[2], rgba[3]);
R2D_Image2dQuad(position, texcoords, pic);
r2d_be_flags = 0;
}
static float MN_drawstring(vec2_t position, const char *text, float height, vec4_t rgba, unsigned int be_flags)
{
float px, py, ix;
unsigned int codeflags, codepoint;
conchar_t buffer[2048], *str = buffer;
COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), false);
Font_BeginScaledString(font_default, position[0], position[1], height, height, &px, &py);
ix=px;
while(*str)
{
str = Font_Decode(str, &codeflags, &codepoint);
px = Font_DrawScaleChar(px, py, codeflags, codepoint);
}
Font_EndString(font_default);
return ((px-ix)*(float)vid.width)/(float)vid.rotpixelwidth;
}
static float MN_stringwidth(const char *text, float height)
{
float px, py;
conchar_t buffer[2048], *end;
end = COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), false);
Font_BeginScaledString(font_default, 0, 0, height, height, &px, &py);
px = Font_LineScaleWidth(buffer, end);
Font_EndString(font_default);
return (px * (float)vid.width) / (float)vid.rotpixelwidth;
}
static void MN_drawsetcliparea(float x, float y, float width, float height)
{
srect_t srect;
if (R2D_Flush)
R2D_Flush();
srect.x = x / (float)vid.fbvwidth;
srect.y = y / (float)vid.fbvheight;
srect.width = width / (float)vid.fbvwidth;
srect.height = height / (float)vid.fbvheight;
srect.dmin = -99999;
srect.dmax = 99999;
srect.y = (1-srect.y) - srect.height;
BE_Scissor(&srect);
}
static void MN_drawresetcliparea(void)
{
if (R2D_Flush)
R2D_Flush();
BE_Scissor(NULL);
}
static qboolean MN_setkeydest(qboolean focused)
{
qboolean ret = Key_Dest_Has(kdm_nmenu);
if (ret == focused)
return false;
if (focused)
{
if (key_dest_absolutemouse & kdm_nmenu)
{ //we're activating the mouse cursor now... make sure the position is actually current.
struct menu_inputevent_args_s ev = {MIE_MOUSEABS, -1};
ev.mouse.screen[0] = mousecursor_x;
ev.mouse.screen[1] = mousecursor_y;
mn_entry->InputEvent(ev);
}
Key_Dest_Add(kdm_nmenu);
}
else
Key_Dest_Remove(kdm_nmenu);
return true;
}
static int MN_getkeydest(void)
{
if (Key_Dest_Has(kdm_nmenu))
{
if (Key_Dest_Has_Higher(kdm_nmenu))
return -1;
return 1;
}
return 0;
}
static int MN_setmousetarget(const char *cursorname, float hot_x, float hot_y, float scale)
{
if (cursorname)
{
struct key_cursor_s *m = &key_customcursor[kc_nmenu];
if (scale <= 0)
scale = 1;
if (!strcmp(m->name, cursorname) || m->hotspot[0] != hot_x || m->hotspot[1] != hot_y || m->scale != scale)
{
Q_strncpyz(m->name, cursorname, sizeof(m->name));
m->hotspot[0] = hot_x;
m->hotspot[1] = hot_y;
m->scale = scale;
m->dirty = true;
}
key_dest_absolutemouse |= kdm_nmenu;
}
else
key_dest_absolutemouse &= ~kdm_nmenu;
return true;
}
void MN_Shutdown(void)
{
Key_Dest_Remove(kdm_nmenu);
if (mn_entry)
{
mn_entry->Shutdown();
mn_entry = NULL;
}
if (libmenu)
{
Sys_CloseLibrary(libmenu);
libmenu = NULL;
}
}
qboolean MN_Init(void)
{
menu_export_t *(QDECL *pGetMenuAPI) ( menu_import_t *import );
static menu_import_t imports =
{
NATIVEMENU_API_VERSION_MAX,
MN_checkextension,
Host_Error,
Con_Printf,
Con_DPrintf,
MN_localcmd,
Cvar_VariableValue,
Cvar_VariableString,
Cvar_SetNamed,
MN_registercvar,
MN_getserverstate,
MN_getclientstate,
S_LocalSound2,
// file input / search crap
FS_OpenVFS,
MN_fclose,
VFS_GETS,
VFS_PRINTF,
COM_EnumerateFiles,
// Drawing stuff
MN_precache_pic,
MN_drawgetimagesize,
MN_drawquad,
MN_drawstring,
MN_stringwidth,
MN_drawsetcliparea,
MN_drawresetcliparea,
// Menu specific stuff
MN_setkeydest,
MN_getkeydest,
MN_setmousetarget,
Key_KeynumToString,
Key_StringToKeynum,
M_FindKeysForBind,
// Server browser stuff
NULL,//MN_gethostcachevalue,
Master_ReadKeyString,
Master_ReadKeyFloat,
Master_ClearMasks,
Master_SetMaskString,
Master_SetMaskInteger,
Master_SetSortField,
Master_SortServers,
Master_SortedServer,
MasterInfo_Refresh,
Master_KeyForName,
};
dllfunction_t funcs[] =
{
{(void*)&pGetMenuAPI, "GetMenuAPI"},
{NULL}
};
void *iterator = NULL;
char syspath[MAX_OSPATH];
char gamepath[MAX_QPATH];
while(COM_IteratePaths(&iterator, syspath, sizeof(syspath), gamepath, sizeof(gamepath)))
{
if (!com_nogamedirnativecode.ival)
libmenu = Sys_LoadLibrary(va("%smenu_"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, syspath), funcs);
if (libmenu)
break;
if (host_parms.binarydir && !strchr(gamepath, '/') && !strchr(gamepath, '\\'))
libmenu = Sys_LoadLibrary(va("%smenu_%s_"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, host_parms.binarydir, gamepath), funcs);
if (libmenu)
break;
//some build systems don't really know the cpu type.
if (host_parms.binarydir && !strchr(gamepath, '/') && !strchr(gamepath, '\\'))
libmenu = Sys_LoadLibrary(va("%smenu_%s" ARCH_DL_POSTFIX, host_parms.binarydir, gamepath), funcs);
if (libmenu)
break;
}
if (libmenu)
{
key_dest_absolutemouse |= kdm_nmenu;
mn_entry = pGetMenuAPI (&imports);
if (mn_entry && mn_entry->api_version >= NATIVEMENU_API_VERSION_MIN && mn_entry->api_version <= NATIVEMENU_API_VERSION_MAX)
{
mn_entry->Init();
return true;
}
else
mn_entry = NULL;
MN_Shutdown();
Sys_CloseLibrary(libmenu);
libmenu = NULL;
}
return false;
}
#endif

View file

@ -3638,8 +3638,12 @@ void M_Menu_ModelViewer_f(void)
typedef struct
{
ftemanifest_t **manifests;
size_t nummanifests;
struct
{
ftemanifest_t *manifest;
char *gamedir;
} *mod;
size_t nummods;
int y;
} modmenu_t;
@ -3652,7 +3656,7 @@ static void Mods_Draw(int x, int y, struct menucustom_s *c, struct menu_s *m)
mods->y = y;
if (!mods->nummanifests)
if (!mods->nummods)
{
Draw_FunString(x, y+0, "No games or mods known");
#if defined(FTE_TARGET_WEB) || defined(NACL)
@ -3665,12 +3669,22 @@ static void Mods_Draw(int x, int y, struct menucustom_s *c, struct menu_s *m)
return;
}
for (i = 0; y+8 <= ym && i < mods->nummanifests; y+=8, i++)
for (i = 0; y+8 <= ym && i < mods->nummods; y+=8, i++)
{
if (mods->mod[i].manifest)
{
if (mousecursor_y >= y && mousecursor_y < y+8)
Draw_AltFunString(x, y, mods->manifests[i]->formalname);
Draw_AltFunString(x, y, mods->mod[i].manifest->formalname);
else
Draw_FunString(x, y, mods->manifests[i]->formalname);
Draw_FunString(x, y, mods->mod[i].manifest->formalname);
}
else
{
if (mousecursor_y >= y && mousecursor_y < y+8)
Draw_AltFunString(x, y, mods->mod[i].gamedir);
else
Draw_FunString(x, y, mods->mod[i].gamedir);
}
}
}
static qboolean Mods_Key(struct menucustom_s *c, struct menu_s *m, int key, unsigned int unicode)
@ -3678,14 +3692,14 @@ static qboolean Mods_Key(struct menucustom_s *c, struct menu_s *m, int key, unsi
modmenu_t *mods = c->dptr;
int i;
ftemanifest_t *man;
if (key == K_MOUSE1)
if (key == K_MOUSE1 || key == K_ENTER || key == K_GP_A)
{
qboolean wasgameless = !*FS_GetGamedir(false);
i = (mousecursor_y - mods->y)/8;
if (i < 0 || i > mods->nummanifests)
if (i < 0 || i > mods->nummods)
return false;
man = mods->manifests[i];
mods->manifests[i] = NULL; //make sure the manifest survives the menu being closed.
man = mods->mod[i].manifest;
mods->mod[i].manifest = NULL; //make sure the manifest survives the menu being closed.
M_RemoveMenu(m);
FS_ChangeGame(man, true, true);
@ -3711,22 +3725,60 @@ static void Mods_Remove (struct menu_s *m)
modmenu_t *mods = m->data;
int i;
for (i = 0; i < mods->nummanifests; i++)
for (i = 0; i < mods->nummods; i++)
{
if (mods->manifests[i])
FS_Manifest_Free(mods->manifests[i]);
if (mods->mod[i].manifest)
FS_Manifest_Free(mods->mod[i].manifest);
Z_Free(mods->mod[i].gamedir);
}
Z_Free(mods->manifests);
mods->manifests = NULL;
Z_Free(mods->mod);
mods->mod = NULL;
}
static qboolean Mods_AddMod(void *usr, ftemanifest_t *man)
static qboolean Mods_AddManifest(void *usr, ftemanifest_t *man)
{
modmenu_t *mods = usr;
int i = mods->nummanifests;
mods->manifests = BZ_Realloc(mods->manifests, (i+1) * sizeof(*mods->manifests));
mods->manifests[i] = man;
mods->nummanifests = i+1;
int i = mods->nummods;
mods->mod = BZ_Realloc(mods->mod, (i+1) * sizeof(*mods->mod));
mods->mod[i].manifest = man;
mods->mod[i].gamedir = NULL;
mods->nummods = i+1;
return true;
}
static int QDECL Mods_AddGamedir(const char *fname, qofs_t fsize, time_t mtime, void *usr, searchpathfuncs_t *spath)
{
modmenu_t *mods = usr;
size_t l = strlen(fname);
int i, p;
char gamedir[MAX_QPATH];
if (l && fname[l-1] == '/' && l < countof(gamedir))
{
l--;
memcpy(gamedir, fname, l);
gamedir[l] = 0;
for (i = 0; i < mods->nummods; i++)
{
//don't add dupes (can happen from gamedir+homedir)
//if the gamedir was already included in one of the manifests, don't bother including it again.
//this generally removes id1.
if (mods->mod[i].manifest)
{
for (p = 0; p < countof(fs_manifest->gamepath); p++)
if (mods->mod[i].manifest->gamepath[p].path)
if (!Q_strcasecmp(mods->mod[i].manifest->gamepath[p].path, gamedir))
return true;
}
else if (mods->mod[i].gamedir)
{
if (!Q_strcasecmp(mods->mod[i].gamedir, gamedir))
return true;
}
}
mods->mod = BZ_Realloc(mods->mod, (i+1) * sizeof(*mods->mod));
mods->mod[i].manifest = NULL;
mods->mod[i].gamedir = Z_StrDup(gamedir);
mods->nummods = i+1;
}
return true;
}
@ -3737,9 +3789,16 @@ void M_Menu_Mods_f (void)
modmenu_t mods;
menucustom_t *c;
menu_t *menu;
extern qboolean com_homepathenabled;
memset(&mods, 0, sizeof(mods));
FS_EnumerateKnownGames(Mods_AddMod, &mods);
FS_EnumerateKnownGames(Mods_AddManifest, &mods);
if (com_homepathenabled)
Sys_EnumerateFiles(com_homepath, "*", Mods_AddGamedir, &mods, NULL);
Sys_EnumerateFiles(com_gamepath, "*", Mods_AddGamedir, &mods, NULL);
//FIXME: sort by mtime?
Key_Dest_Add(kdm_emenu);

View file

@ -196,6 +196,74 @@ int M_FindKeysForCommand (int bindmap, int pnum, const char *command, int *keyli
return M_FindKeysForBind(bindmap, va("%s%s", prefix, command), keylist, keymods, keycount);
}
/*
================
M_ToggleMenu_f
================
*/
void M_ToggleMenu_f (void)
{
#ifndef NOBUILTINMENUS
if (topmenu)
{
Key_Dest_Add(kdm_emenu);
return;
}
#endif
#ifdef CSQC_DAT
if (CSQC_ConsoleCommand(-1, "togglemenu"))
{
Key_Dest_Remove(kdm_console|kdm_cwindows);
return;
}
#endif
#ifdef MENU_DAT
if (MP_Toggle(1))
{
Key_Dest_Remove(kdm_console|kdm_cwindows);
return;
}
#endif
#ifdef MENU_NATIVECODE
if (mn_entry)
{
mn_entry->Toggle(1);
Key_Dest_Remove(kdm_console|kdm_cwindows);
return;
}
#endif
#ifdef VM_UI
if (UI_OpenMenu())
return;
#endif
#ifndef NOBUILTINMENUS
M_Menu_Main_f ();
Key_Dest_Remove(kdm_console|kdm_cwindows);
#endif
}
/*
================
M_Restart_f
================
*/
void M_Init_Internal (void);
void M_Restart_f(void)
{
M_Shutdown(false);
if (!strcmp(Cmd_Argv(1), "off"))
{ //explicitly restart the engine's menu. not the menuqc crap
//don't even start csqc menus.
M_Init_Internal();
}
else
M_Reinit();
}
#ifndef NOBUILTINMENUS
void M_Menu_Audio_f (void);
@ -318,61 +386,6 @@ void M_CloseMenu_f (void)
M_RemoveAllMenus(false);
Key_Dest_Remove(kdm_emenu);
}
/*
================
M_ToggleMenu_f
================
*/
void M_ToggleMenu_f (void)
{
if (topmenu)
{
Key_Dest_Add(kdm_emenu);
return;
}
#ifdef CSQC_DAT
if (CSQC_ConsoleCommand(-1, "togglemenu"))
{
Key_Dest_Remove(kdm_console|kdm_cwindows);
return;
}
#endif
#ifdef MENU_DAT
if (MP_Toggle(1))
{
Key_Dest_Remove(kdm_console|kdm_cwindows);
return;
}
#endif
#ifdef VM_UI
if (UI_OpenMenu())
return;
#endif
//it IS a toggle, so close the menu if its already active
if (Key_Dest_Has(kdm_emenu))
{
Key_Dest_Remove(kdm_emenu);
return;
}
if (Key_Dest_Has(kdm_console|kdm_cwindows))
Key_Dest_Remove(kdm_console|kdm_cwindows);
/*
{
if (cls.state != ca_active)
{
Key_Dest_Remove(kdm_console);
M_Menu_Main_f();
}
else
Con_ToggleConsole_Force ();
}
else*/
{
M_Menu_Main_f ();
}
}
//=============================================================================
/* KEYS MENU */
@ -1316,6 +1329,9 @@ void M_DeInit_Internal (void)
void M_Shutdown(qboolean total)
{
#ifdef MENU_NATIVECODE
MN_Shutdown();
#endif
#ifdef MENU_DAT
MP_Shutdown();
#endif
@ -1325,6 +1341,9 @@ void M_Shutdown(qboolean total)
void M_Reinit(void)
{
#ifdef MENU_NATIVECODE
if (!MN_Init())
#endif
#ifdef MENU_DAT
if (!MP_Init())
#endif
@ -1341,7 +1360,7 @@ void M_MenuPop_f(void);
//menu.dat is loaded later... after the video and everything is up.
void M_Init (void)
{
Cmd_AddCommand("menu_restart", M_Restart_f);
Cmd_AddCommand("togglemenu", M_ToggleMenu_f);
Cmd_AddCommand("closemenu", M_CloseMenu_f);
Cmd_AddCommand("fps_preset", FPS_Preset_f);
@ -1380,12 +1399,18 @@ void M_Init_Internal (void){}
void M_DeInit_Internal (void){}
void M_Shutdown(qboolean total)
{
#ifdef MENU_NATIVECODE
MN_Shutdown();
#endif
#ifdef MENU_DAT
MP_Shutdown();
#endif
}
void M_Reinit(void)
{
#ifdef MENU_NATIVECODE
if (!MN_Init())
#endif
#ifdef MENU_DAT
if (!MP_Init())
#endif
@ -1395,6 +1420,9 @@ void M_Reinit(void)
}
void M_Init (void)
{
Cmd_AddCommand("menu_restart", M_Restart_f);
Cmd_AddCommand("togglemenu", M_ToggleMenu_f);
Media_Init();
M_Reinit();
}

View file

@ -110,7 +110,6 @@ void M_Shutdown(qboolean total);
void M_Keydown (int key, int unicode);
void M_Keyup (int key, int unicode);
void M_Draw (int uimenu);
void M_ToggleMenu_f (void);
void M_Menu_Mods_f (void); //used at startup if the current gamedirs look dodgy.
void M_Menu_Installer (void); //given an embedded manifest, this displays an install menu for said game.
mpic_t *M_CachePic (char *path);
@ -454,7 +453,6 @@ void M_UnbindCommand (const char *command);
//no builtin menu code.
//stubs
#define M_Menu_Prompt(cb,ctx,messages,optionyes,optionno,optioncancel) (cb)(ctx,-1)
#define M_ToggleMenu_f() Cbuf_AddText("togglemenu\n",RESTRICT_LOCAL)
//#define M_Shutdown(t) MP_Shutdown()
void M_Init (void);
@ -466,6 +464,7 @@ void M_Draw (int uimenu);
#endif
int M_FindKeysForCommand (int bindmap, int pnum, const char *command, int *keylist, int *keymods, int keycount);
int M_FindKeysForBind (int bindmap, const char *command, int *keylist, int *keymods, int keycount);
void M_ToggleMenu_f (void);
#ifdef MENU_DAT
void MP_CvarChanged(cvar_t *var);
@ -484,6 +483,13 @@ int MP_BuiltinValid(const char *name, int num);
qboolean MP_ConsoleCommand(const char *cmdtext);
#endif
#ifdef MENU_NATIVECODE
#include "../native/api_menu.h"
extern menu_export_t *mn_entry;
void MN_Shutdown(void);
qboolean MN_Init(void);
#endif
#define MGT_BAD ~0
#define MGT_QUAKE1 0
#define MGT_HEXEN2 1

View file

@ -1067,7 +1067,7 @@ void Master_ResortServer(serverinfo_t *server)
}
}
void Master_SortServers(void)
int Master_SortServers(void)
{
serverinfo_t *server;
@ -1088,6 +1088,8 @@ void Master_SortServers(void)
{
Master_ResortServer(server);
}
return numvisibleservers;
}
serverinfo_t *Master_SortedServer(int idx)
@ -1104,7 +1106,7 @@ int Master_NumSorted(void)
}
float Master_ReadKeyFloat(serverinfo_t *server, int keynum)
float Master_ReadKeyFloat(serverinfo_t *server, hostcachekey_t keynum)
{
if (!server)
return -1;
@ -1164,7 +1166,7 @@ void Master_DecodeColour(vec3_t ret, int col)
VectorSet(ret, ((col&0xff0000)>>16)/255.0, ((col&0x00ff00)>>8)/255.0, ((col&0x0000ff)>>0)/255.0);
}
char *Master_ReadKeyString(serverinfo_t *server, int keynum)
char *Master_ReadKeyString(serverinfo_t *server, hostcachekey_t keynum)
{
static char adr[MAX_ADR_SIZE];
@ -1237,7 +1239,7 @@ char *Master_ReadKeyString(serverinfo_t *server, int keynum)
return "";
}
int Master_KeyForName(const char *keyname)
hostcachekey_t Master_KeyForName(const char *keyname)
{
int i;
if (!strcmp(keyname, "map"))

View file

@ -7804,14 +7804,12 @@ qboolean CSQC_DrawView(void)
if (csqcg.frametime)
{
if (csqc_isdarkplaces)
if (1)//csqc_isdarkplaces)
{
static double oldtime;
if (cl.paused)
*csqcg.frametime = 0; //apparently people can't cope with microstutter when they're using this as a test to see if the game is paused.
else
*csqcg.frametime = bound(0, cl.time - oldtime, 0.1);
oldtime = cl.time;
*csqcg.frametime = bound(0, cl.time - cl.lasttime, 0.1);
}
else
*csqcg.frametime = host_frametime;

View file

@ -2700,19 +2700,6 @@ void MP_CoreDump_f(void)
}
}
void MP_Reload_f(void)
{
M_Shutdown(false);
if (!strcmp(Cmd_Argv(1), "off"))
{ //explicitly restart the engine's menu. not the menuqc crap
//don't even start csqc menus.
M_Init_Internal();
}
else
M_Reinit();
}
static void MP_Poke_f(void)
{
/*if (!SV_MayCheat())
@ -2751,7 +2738,6 @@ void MP_Breakpoint_f(void)
void MP_RegisterCvarsAndCmds(void)
{
Cmd_AddCommand("coredump_menuqc", MP_CoreDump_f);
Cmd_AddCommand("menu_restart", MP_Reload_f);
Cmd_AddCommand("menu_cmd", MP_GameCommand_f);
Cmd_AddCommand("breakpoint_menu", MP_Breakpoint_f);
Cmd_AddCommand("loadfont", CL_LoadFont_f);

View file

@ -321,8 +321,8 @@ void Host_InitCommands (void);
void Host_Init (quakeparms_t *parms);
void Host_FinishInit(void);
void Host_Shutdown(void);
NORETURN void VARGS Host_Error (char *error, ...) LIKEPRINTF(1);
NORETURN void VARGS Host_EndGame (char *message, ...) LIKEPRINTF(1);
NORETURN void VARGS Host_Error (const char *error, ...) LIKEPRINTF(1);
NORETURN void VARGS Host_EndGame (const char *message, ...) LIKEPRINTF(1);
qboolean Host_SimulationTime(float time);
double Host_Frame (double time);
qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file);

View file

@ -1734,7 +1734,12 @@ static void Surf_BuildLightMap_Worker (model_t *wmodel, msurface_t *surf, qbyte
else
{
// set to full bright if no light data
if (!surf->samples || !wmodel->lightdata)
if (r_fullbright.ival)
{
for (i=0 ; i<size ; i++)
blocklights[i] = 255*256;
}
else if (!wmodel->lightdata)
{
for (i=0 ; i<size*3 ; i++)
{
@ -1743,10 +1748,10 @@ static void Surf_BuildLightMap_Worker (model_t *wmodel, msurface_t *surf, qbyte
surf->cached_light[0] = d_lightstylevalue[0];
surf->cached_colour[0] = cl_lightstyle[0].colourkey;
}
else if (r_fullbright.ival)
else if (!surf->samples)
{
for (i=0 ; i<size ; i++)
blocklights[i] = 255*256;
blocklights[i] = 0;
}
else
{
@ -2980,8 +2985,10 @@ void Surf_GenBrushBatches(batch_t **batches, entity_t *ent)
struct webostate_s
{
char dbgid[12];
struct webostate_s *next;
model_t *wmodel;
int cluster[2];
qboolean generating;
pvsbuffer_t pvs;
vboarray_t ebo;
void *ebomem;
@ -2989,6 +2996,8 @@ struct webostate_s
int numbatches;
int lightstylevalues[MAX_LIGHTSTYLES]; //when using workers that only reprocessing lighting at 10fps, things get too ugly when things go out of sync
vec3_t lastpos;
batch_t *rbatches[SHADER_SORT_COUNT];
struct wesbatch_s
@ -3002,7 +3011,7 @@ struct webostate_s
vbo_t vbo;
} batches[1];
};
static struct webostate_s *webostate;
static struct webostate_s *webostates;
static struct webostate_s *webogenerating;
static int webogeneratingstate; //1 if generating, 0 if not, for waiting for sync.
static void R_DestroyWorldEBO(struct webostate_s *es)
@ -3028,8 +3037,9 @@ void R_GeneratedWorldEBO(void *ctx, void *data, size_t a_, size_t b_)
batch_t *b, *batch;
mesh_t *m;
int sortid;
R_DestroyWorldEBO(webostate);
webostate = ctx;
struct webostate_s *webostate = ctx;
webostate->next = webostates;
webostates = webostate;
webogenerating = NULL;
webogeneratingstate = 0;
mod = webostate->wmodel;
@ -3097,7 +3107,9 @@ void R_GeneratedWorldEBO(void *ctx, void *data, size_t a_, size_t b_)
memset(m, 0, sizeof(*m));
if (b->shader->flags & SHADER_NEEDSARRAYS)
{
{ //this ebo cache stuff tracks only indexes, we don't know the actual surfs any more.
//if NEEDSARRAYS is flagged then the cpu will need access to the mesh data - which it doesn't have.
//while we could figure out this info, there would be a lot of vertexes that are not referenced, which would be horrendously slow.
if (b->shader->flags & SHADER_SKY)
continue;
b->shader = R_RegisterShader_Vertex("unsupported");
@ -3286,10 +3298,29 @@ void Surf_DrawWorld (void)
#ifdef THREADEDWORLD
if ((r_dynamic.ival < 0 || currentmodel->numbatches) && !r_refdef.recurse && currentmodel->type == mod_brush)
{
if (webostate && webostate->wmodel != currentmodel)
struct webostate_s *webostate, *best = NULL, *generating = NULL;
vec_t bestdist = FLT_MAX;
for (webostate = webostates; webostate; webostate = webostate->next)
{
R_DestroyWorldEBO(webostate);
webostate = NULL;
if (webostate->wmodel != currentmodel)
continue;
if (webostate->cluster[0] == r_viewcluster && webostate->cluster[1] == r_viewcluster2)
{
best = webostate;
break;
}
else
{
vec3_t m;
float d;
VectorSubtract(webostate->lastpos, r_refdef.vieworg, m);
d = DotProduct(m,m);
if (bestdist > d)
{
bestdist = d;
best = webostate;
}
}
}
if (qrenderer != QR_OPENGL && qrenderer != QR_VULKAN)
@ -3304,7 +3335,7 @@ void Surf_DrawWorld (void)
if (webostate->lightstylevalues[i] != d_lightstylevalue[i])
break;
}
if (webostate && webostate->cluster[0] == r_viewcluster && webostate->cluster[1] == r_viewcluster2 && i == MAX_LIGHTSTYLES)
if (webostate && i == MAX_LIGHTSTYLES)
{
}
else
@ -3542,8 +3573,12 @@ void Surf_DeInit(void)
#ifdef THREADEDWORLD
while(webogenerating)
COM_WorkerPartialSync(webogenerating, &webogeneratingstate, true);
while (webostates)
{
void *webostate = webostates;
webostates = webostates->next;
R_DestroyWorldEBO(webostate);
webostate = NULL;
}
#endif
for (i = 0; i < numlightmaps; i++)
@ -3578,13 +3613,19 @@ void Surf_Clear(model_t *mod)
// return;/*they're on the hunk*/
#ifdef THREADEDWORLD
struct webostate_s **link, *t;
while(webogenerating)
COM_WorkerPartialSync(webogenerating, &webogeneratingstate, true);
if (webostate && webostate->wmodel == mod)
for (link = &webostates; (t=*link); )
{
R_DestroyWorldEBO(webostate);
webostate = NULL;
if (t->wmodel == mod)
{
*link = t->next;
R_DestroyWorldEBO(t);
}
else
link = &(*link)->next;
}
#endif
@ -4076,8 +4117,12 @@ void Surf_ClearLightmaps(void)
#ifdef THREADEDWORLD
while(webogenerating)
COM_WorkerPartialSync(webogenerating, &webogeneratingstate, true);
while (webostates)
{
void *webostate = webostates;
webostates = webostates->next;
R_DestroyWorldEBO(webostate);
webostate = NULL;
}
#endif
}
@ -4215,6 +4260,10 @@ TRACE(("dbg: Surf_NewMap: tp\n"));
{
vec3_t mins, maxs;
//fixme: no rotation
if (!cl_static_entities[i].ent.model && cl_static_entities[i].mdlidx > 0 && cl_static_entities[i].mdlidx < countof(cl.model_precache))
cl_static_entities[i].ent.model = cl.model_precache[cl_static_entities[i].mdlidx];
else if (!cl_static_entities[i].ent.model && cl_static_entities[i].mdlidx < 0 && (-cl_static_entities[i].mdlidx) < countof(cl.model_csqcprecache))
cl_static_entities[i].ent.model = cl.model_csqcprecache[-cl_static_entities[i].mdlidx];
if (cl_static_entities[i].ent.model)
{
//unfortunately, we need to know the actual size so that we can get this right. bum.

View file

@ -227,7 +227,7 @@ cvar_t scr_conalpha = CVARC ("scr_conalpha", "0.7",
Cvar_Limiter_ZeroToOne_Callback);
cvar_t scr_consize = CVAR ("scr_consize", "0.5");
cvar_t scr_conspeed = CVAR ("scr_conspeed", "2000");
// 10 - 170
cvar_t scr_fov_mode = CVARFD ("scr_fov_mode", "0", CVAR_ARCHIVE, "Controls what the fov cvar actually controls:\n0: largest axis (ultra-wide monitors means less height will be visible).\n1: smallest axis (ultra-wide monitors will distort at the edges).\n2: horizontal axis.\n3: vertical axis.");
cvar_t scr_fov = CVARFCD("fov", "90", CVAR_ARCHIVE, SCR_Fov_Callback,
"field of vision, 1-170 degrees, standard fov is 90, nquake defaults to 108.");
cvar_t scr_fov_viewmodel = CVARFD("r_viewmodel_fov", "", CVAR_ARCHIVE,
@ -904,6 +904,7 @@ void Renderer_Init(void)
Cvar_Register(&scr_viewsize, SCREENOPTIONS);
Cvar_Register(&scr_fov, SCREENOPTIONS);
Cvar_Register(&scr_fov_viewmodel, SCREENOPTIONS);
Cvar_Register(&scr_fov_mode, SCREENOPTIONS);
Cvar_Register (&scr_sshot_type, SCREENOPTIONS);
Cvar_Register (&scr_sshot_compression, SCREENOPTIONS);

View file

@ -1166,15 +1166,67 @@ float CalcFov (float fov_x, float width, float height)
return a;
}
static void V_CalcAFov(float afov, float *x, float *y, float w, float h)
{
extern cvar_t scr_fov_mode;
extern cvar_t r_stereo_separation;
int mode = scr_fov_mode.ival;
#ifdef FTE_TARGET_WEB
if (r_refdef.stereomethod == STEREO_WEBVR)
w *= 0.5;
#endif
if (r_refdef.stereomethod == STEREO_CROSSEYED && r_stereo_separation.value)
w *= 0.5;
afov = bound(0.001, afov, 170);
restart:
switch(mode)
{
default:
case 0: //maj-
if (h > w)
{ //fov specified is interpreted as the horizontal fov at quake's original res: 640*432 (ie: with the sbar removed)...
// afov = CalcFov(afov, 640, 432);
mode = 3; //vertical
}
else
mode = 2; //horizontal
goto restart;
case 1: //min+
if (h < w)
mode = 3; //vertical
else
mode = 2; //horizontal
goto restart;
case 2: //horizontal
*x = afov;
*y = CalcFov(afov, w, h);
if (*y > 170)
{
*y = 170;
*x = CalcFov(170, h, w);
}
break;
case 3: //vertical
*y = afov;
*x = CalcFov(afov, h, w);
if (*x > 170)
{ //don't allow screwed fovs.
*x = 170;
*y = CalcFov(afov, w, h);
}
break;
}
}
void V_ApplyAFov(playerview_t *pv)
{
//explicit fov overrides aproximate fov.
//aproximate fov is our regular fov value. explicit is settable by gamecode for weird aspect ratios
if (!r_refdef.fov_x || !r_refdef.fov_y)
{
extern cvar_t r_stereo_separation;
float ws;
float afov = r_refdef.afov;
if (!afov) //make sure its sensible.
{
@ -1184,59 +1236,15 @@ void V_ApplyAFov(playerview_t *pv)
afov *= pv->statsf[STAT_VIEWZOOM]/STAT_VIEWZOOM_SCALE;
#endif
}
afov = bound(0.001, afov, 170);
ws = 1;
#ifdef FTE_TARGET_WEB
if (r_refdef.stereomethod == STEREO_WEBVR)
ws = 0.5;
#endif
if (r_refdef.stereomethod == STEREO_CROSSEYED && r_stereo_separation.value)
ws = 0.5;
//attempt to retain a classic fov
if (ws*r_refdef.vrect.width < (r_refdef.vrect.height*640)/432)
{
r_refdef.fov_y = CalcFov(afov, (ws*r_refdef.vrect.width*r_refdef.pxrect.width)/vid.fbvwidth, (r_refdef.vrect.height*r_refdef.pxrect.height)/vid.fbvheight);
r_refdef.fov_x = afov;//CalcFov(r_refdef.fov_y, 432, 640);
}
else
{
r_refdef.fov_y = CalcFov(afov, 640, 432);
r_refdef.fov_x = CalcFov(r_refdef.fov_y, r_refdef.vrect.height, r_refdef.vrect.width*ws);
}
V_CalcAFov(afov, &r_refdef.fov_x, &r_refdef.fov_y, (r_refdef.vrect.width*r_refdef.pxrect.width)/vid.fbvwidth, (r_refdef.vrect.height*r_refdef.pxrect.height)/vid.fbvheight);
}
if (!r_refdef.fovv_x || !r_refdef.fovv_y)
{
extern cvar_t r_stereo_separation;
float ws;
float afov = scr_fov_viewmodel.value;
if (afov)
{
afov = bound(0.001, afov, 170);
ws = 1;
#ifdef FTE_TARGET_WEB
if (r_refdef.stereomethod == STEREO_WEBVR)
ws = 0.5;
#endif
if (r_refdef.stereomethod == STEREO_CROSSEYED && r_stereo_separation.value)
ws = 0.5;
//attempt to retain a classic fov
if (ws*r_refdef.vrect.width < (r_refdef.vrect.height*640)/432)
{
r_refdef.fovv_y = CalcFov(afov, (ws*r_refdef.vrect.width*r_refdef.pxrect.width)/vid.fbvwidth, (r_refdef.vrect.height*r_refdef.pxrect.height)/vid.fbvheight);
r_refdef.fovv_x = afov;//CalcFov(r_refdef.fov_y, 432, 640);
}
else
{
r_refdef.fovv_y = CalcFov(afov, 640, 432);
r_refdef.fovv_x = CalcFov(r_refdef.fovv_y, r_refdef.vrect.height, r_refdef.vrect.width*ws);
}
}
V_CalcAFov(afov, &r_refdef.fovv_x, &r_refdef.fovv_y, (r_refdef.vrect.width*r_refdef.pxrect.width)/vid.fbvwidth, (r_refdef.vrect.height*r_refdef.pxrect.height)/vid.fbvheight);
else
{
r_refdef.fovv_x = r_refdef.fov_x;

View file

@ -558,8 +558,8 @@ typedef struct vfsfile_s
#define VFS_WRITE(vf,buffer,buflen) ((vf)->WriteBytes(vf,buffer,buflen))
#define VFS_FLUSH(vf) do{if((vf)->Flush)(vf)->Flush(vf);}while(0)
#define VFS_PUTS(vf,s) do{const char *t=s;(vf)->WriteBytes(vf,t,strlen(t));}while(0)
char *VFS_GETS(vfsfile_t *vf, char *buffer, int buflen);
void VARGS VFS_PRINTF(vfsfile_t *vf, char *fmt, ...) LIKEPRINTF(2);
char *VFS_GETS(vfsfile_t *vf, char *buffer, size_t buflen);
void VARGS VFS_PRINTF(vfsfile_t *vf, const char *fmt, ...) LIKEPRINTF(2);
enum fs_relative{
FS_BINARYPATH, //for dlls and stuff

View file

@ -650,6 +650,15 @@ char *Cvar_VariableString (const char *var_name)
return var->string;
}
void Cvar_SetNamed (const char *var_name, const char *newvalue)
{
cvar_t *var;
var = Cvar_FindVar (var_name);
if (!var)
return;
Cvar_Set(var, newvalue);
}
/*
============

View file

@ -202,6 +202,8 @@ float Cvar_VariableValue (const char *var_name);
char *Cvar_VariableString (const char *var_name);
// returns an empty string if not defined
void Cvar_SetNamed (const char *var_name, const char *newvalue);
char *Cvar_CompleteVariable (const char *partial);
// attempts to match a partial variable name for command line completion
// returns NULL if nothing fits

View file

@ -114,14 +114,14 @@ void FS_UnRegisterFileSystemModule(void *module)
}
}
char *VFS_GETS(vfsfile_t *vf, char *buffer, int buflen)
char *VFS_GETS(vfsfile_t *vf, char *buffer, size_t buflen)
{
char in;
char *out = buffer;
int len;
len = buflen-1;
if (len == 0)
size_t len;
if (buflen <= 1)
return NULL;
len = buflen-1;
while (len > 0)
{
if (VFS_READ(vf, &in, 1) != 1)
@ -146,7 +146,7 @@ char *VFS_GETS(vfsfile_t *vf, char *buffer, int buflen)
return buffer;
}
void VARGS VFS_PRINTF(vfsfile_t *vf, char *format, ...)
void VARGS VFS_PRINTF(vfsfile_t *vf, const char *format, ...)
{
va_list argptr;
char string[1024];
@ -4462,8 +4462,8 @@ static int FS_IdentifyDefaultGameFromDir(const char *basedir)
//attempt to work out which game we're meant to be trying to run based upon a few things
//1: fs_changegame console command override. fixme: needs to cope with manifests too.
//2: -quake3 argument implies that the user wants to run quake3.
//3: if we are ftequake3.exe then we always try to run quake3.
//2: -quake3 (etc) argument implies that the user wants to run quake3.
//3: otherwise if we are ftequake3.exe then we try to run quake3.
//4: identify characteristic files within the working directory (like id1/pak0.pak implies we're running quake)
//5: check where the exe actually is instead of simply where we're being run from.
//6: try the homedir, just in case.

View file

@ -3932,12 +3932,32 @@ struct strbuf {
char **strings;
size_t used;
size_t allocated;
int flags;
};
#define BUFFLAG_SAVED 1
#define BUFSTRBASE 1
struct strbuf *strbuflist;
size_t strbufmax;
static void PR_buf_savegame(vfsfile_t *f, pubprogfuncs_t *prinst, qboolean binary)
{
size_t i, bufno;
for (bufno = 0; bufno < strbufmax; bufno++)
{
if (strbuflist[bufno].prinst == prinst && (strbuflist[bufno].flags & BUFFLAG_SAVED))
{
VFS_PRINTF (f, "buffer %i %i %i %i\n", bufno, 1, ev_string, strbuflist[bufno].used);
VFS_PRINTF (f, "{\n");
for (i = 0; i < strbuflist[bufno].used; i++)
if (strbuflist[bufno].strings[i])
VFS_PRINTF (f, "%i %s\n", i, strbuflist[bufno].strings[i]);
VFS_PRINTF (f, "}\n");
}
}
}
void PF_buf_shutdown(pubprogfuncs_t *prinst)
{
size_t i, bufno;
@ -3972,7 +3992,8 @@ void QCBUILTIN PF_buf_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
size_t i;
const char *type = ((prinst->callargc>0)?PR_GetStringOfs(prinst, OFS_PARM0):"string");
// unsigned int flags = ((prinst->callargc>1)?G_FLOAT(OFS_PARM1):1);
unsigned int flags = ((prinst->callargc>1)?G_FLOAT(OFS_PARM1):BUFFLAG_SAVED);
flags &= BUFFLAG_SAVED;
if (!Q_strcasecmp(type, "string"))
;
@ -4001,6 +4022,7 @@ void QCBUILTIN PF_buf_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
strbuflist[i].used = 0;
strbuflist[i].allocated = 0;
strbuflist[i].strings = NULL;
strbuflist[i].flags = flags;
G_FLOAT(OFS_RETURN) = i+BUFSTRBASE;
}
// #441 void(float bufhandle) buf_del (DP_QC_STRINGBUFFERS)
@ -6239,6 +6261,10 @@ void PR_Common_Shutdown(pubprogfuncs_t *progs, qboolean errored)
#endif
tokenize_flush();
}
void PR_Common_SaveGame(vfsfile_t *f, pubprogfuncs_t *prinst, qboolean binary)
{
PR_buf_savegame(f, prinst, binary);
}
#define DEF_SAVEGLOBAL (1u<<15)

View file

@ -499,6 +499,7 @@ void QCBUILTIN PF_whichpack (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
int QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, char *reason, pbool fatal);
void PR_Common_Shutdown(pubprogfuncs_t *progs, qboolean errored);
void PR_Common_SaveGame(vfsfile_t *f, pubprogfuncs_t *prinst, qboolean binary);
//FIXME
pbool PR_RunWarning (pubprogfuncs_t *ppf, char *error, ...);

View file

@ -5425,6 +5425,8 @@ done:;
for (i = 0; i < s->numpasses; i++)
{
pass = &s->passes[i];
if (pass->prog)
continue;
if (pass->numtcmods || (s->passes[i].tcgen != TC_GEN_BASE && s->passes[i].tcgen != TC_GEN_LIGHTMAP) || !(s->passes[i].flags & SHADER_PASS_NOCOLORARRAY))
{
s->flags |= SHADER_NEEDSARRAYS;

View file

@ -60,9 +60,13 @@ none of these issues will be fixed by a compositing window manager, because ther
#ifdef VKQUAKE
#include "vk/vkrenderer.h"
#ifdef VK_USE_PLATFORM_XLIB_KHR
static qboolean XVK_SetupSurface_XLib(void);
#endif
#ifdef VK_USE_PLATFORM_XCB_KHR
static qboolean XVK_SetupSurface_XCB(void);
#endif
#endif
#ifdef GLQUAKE
#include <GL/glx.h>
#ifdef USE_EGL

View file

@ -1477,7 +1477,7 @@ char *ED_WriteGlobals(progfuncs_t *progfuncs, char *buf, size_t *bufofs, size_t
ddef32_t *def32;
ddef16_t *def16;
unsigned int i;
unsigned int j;
// unsigned int j;
const char *name;
int type;
int curprogs = prinst.pr_typecurrent;
@ -1538,13 +1538,13 @@ char *ED_WriteGlobals(progfuncs_t *progfuncs, char *buf, size_t *bufofs, size_t
v = (int *)&current_progstate->globals[def16->ofs];
// make sure the value is not null, where there's no point in saving
/* // make sure the value is not null, where there's no point in saving
for (j=0 ; j<type_size[type] ; j++)
if (v[j])
break;
if (j == type_size[type])
continue;
*/
add16:
AddS("\""); AddS(name); AddS("\" \""); AddS(PR_UglyValueString(&progfuncs->funcs, def16->type&~DEF_SAVEGLOBAL, (eval_t *)v)); AddS("\"\n");
}
@ -1594,12 +1594,12 @@ char *ED_WriteGlobals(progfuncs_t *progfuncs, char *buf, size_t *bufofs, size_t
v = (int *)&current_progstate->globals[def32->ofs];
// make sure the value is not null, where there's no point in saving
/* // make sure the value is not null, where there's no point in saving
for (j=0 ; j<type_size[type] ; j++)
if (v[j])
break;
if (j == type_size[type])
continue;
continue;*/
add32:
AddS("\""); AddS(name); AddS("\" \""); AddS(PR_UglyValueString(&progfuncs->funcs, def32->type&~DEF_SAVEGLOBAL, (eval_t *)v)); AddS("\"\n");
}
@ -1748,6 +1748,9 @@ char *PR_SaveCallStack (progfuncs_t *progfuncs, char *buf, size_t *bufofs, size_
//there are two ways of saving everything.
//0 is to save just the entities.
//1 is to save the entites, and all the progs info so that all the variables are saved off, and it can be reloaded to exactly how it was (provided no files or data has been changed outside, like the progs.dat for example)
//2 is for vanilla-compatible saved games
//3 is a (human-readable) coredump
//4 is binary saved games.
char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, size_t *bufofs, size_t bufmax, int alldata)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
@ -1764,8 +1767,12 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, size_t *bufofs, size_t b
}
*bufofs = 0;
if (alldata == 2)
{ //special Q1 savegame compatability mode.
switch(alldata)
{
default:
return NULL;
case 2:
//special Q1 savegame compatability mode.
//engine will need to store references to progs type and will need to preload the progs and inti the ents itself before loading.
//Make sure there is only 1 progs loaded.
@ -1785,9 +1792,7 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, size_t *bufofs, size_t b
oldprogs = prinst.pr_typecurrent;
PR_SwitchProgs(progfuncs, 0);
ED_WriteGlobals(progfuncs, buf, bufofs, bufmax);
PR_SwitchProgs(progfuncs, oldprogs);
AddS ("}\n");
@ -1807,10 +1812,11 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, size_t *bufofs, size_t b
}
return buf;
}
if (alldata)
{
case 0: //Writes entities only
break;
case 1:
case 3:
AddS("general {\n");
AddS(qcva("\"maxprogs\" \"%i\"\n", prinst.maxprogs));
// AddS(qcva("\"maxentities\" \"%i\"\n", maxedicts));
@ -1856,6 +1862,7 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, size_t *bufofs, size_t b
}
PR_SwitchProgs(progfuncs, oldprogs);
}
for (a = 0; a < sv_num_edicts; a++)
{
edictrun_t *ed = (edictrun_t *)EDICT_NUM(progfuncs, a);

View file

@ -838,7 +838,7 @@ long ParseNum (char *str)
#define MAXQCCFILES 3
struct {
char name[64];
char *name;
FILE *stdio;
char *buff;
int buffsize;
@ -848,16 +848,11 @@ struct {
int SafeOpenWrite (char *filename, int maxsize)
{
int i;
if (strlen(filename) >= sizeof(qccfile[0].name))
{
QCC_Error(ERR_TOOMANYOPENFILES, "Filename %s too long", filename);
return -1;
}
for (i = 0; i < MAXQCCFILES; i++)
{
if (!qccfile[i].stdio && !qccfile[i].buff)
{
strcpy(qccfile[i].name, filename);
qccfile[i].name = strdup(filename);
qccfile[i].buffsize = maxsize;
qccfile[i].maxofs = 0;
qccfile[i].ofs = 0;
@ -926,6 +921,8 @@ pbool SafeClose(int hand)
ret = externs->WriteFile(qccfile[hand].name, qccfile[hand].buff, qccfile[hand].maxofs);
free(qccfile[hand].buff);
}
free(qccfile[hand].name);
qccfile[hand].name = NULL;
qccfile[hand].buff = NULL;
qccfile[hand].stdio = NULL;
return ret;

View file

@ -6935,6 +6935,41 @@ static QCC_ref_t *QCC_PR_ParseField(QCC_ref_t *refbuf, QCC_ref_t *lhs)
return lhs;
}
//this is more complex than it needs to be, in order to ensure that anon unions/structs can be handled.
struct QCC_typeparam_s *QCC_PR_FindStructMember(QCC_type_t *t, const char *membername, unsigned int *out_ofs)
{
unsigned int nofs;
int i;
struct QCC_typeparam_s *r = NULL, *n;
for (i = 0; i < t->num_parms; i++)
{
if ((!t->params[i].paramname || !*t->params[i].paramname) && (t->params[i].type->type == ev_struct || t->params[i].type->type == ev_union))
{ //anonymous structs/unions can nest
n = QCC_PR_FindStructMember(t->params[i].type, membername, &nofs);
if (n)
{
if (r)
break;
r = n;
*out_ofs = t->params[i].ofs + nofs;
}
}
else if (flag_caseinsensitive?!stricmp (t->params[i].paramname, membername):!STRCMP(t->params[i].paramname, membername))
{
if (r)
break;
r = t->params+i;
*out_ofs = r->ofs;
}
}
if (i < t->num_parms)
{
QCC_PR_ParseError(0, "multiple members found matching %s.%s", t->name, membername);
return NULL;
}
return r;
}
/*checks for:
<d>[X]
<d>[X].foo
@ -7177,8 +7212,9 @@ vectorarrayindex:
else if (((t->type == ev_pointer && !arraysize) || (t->type == ev_field && (t->aux_type->type == ev_struct || t->aux_type->type == ev_union)) || t->type == ev_struct || t->type == ev_union) && (QCC_PR_CheckToken(".") || QCC_PR_CheckToken("->")))
{
char *tname;
unsigned int i;
unsigned int ofs;
pbool fld = t->type == ev_field;
struct QCC_typeparam_s *p;
if (!idx.cast && t->type == ev_pointer && !arraysize)
{
t = t->aux_type;
@ -7200,18 +7236,14 @@ vectorarrayindex:
else
QCC_PR_ParseError(0, "indirection in something that is not a struct or union", tname);
for (i = 0; i < t->num_parms; i++)
{
if (QCC_PR_CheckName(t->params[i].paramname))
break;
}
if (i == t->num_parms)
p = QCC_PR_FindStructMember(t, QCC_PR_ParseName(), &ofs);
if (!p)
QCC_PR_ParseError(0, "%s is not a member of %s", pr_token, tname);
if (!t->params[i].ofs && idx.cast)
if (!ofs && idx.cast)
;
else if (QCC_OPCodeValid(&pr_opcodes[OP_ADD_I]))
{
tmp = QCC_MakeIntConst(t->params[i].ofs);
tmp = QCC_MakeIntConst(ofs);
if (idx.cast)
idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], QCC_SupplyConversion(idx, ev_integer, true), QCC_SupplyConversion(tmp, ev_integer, true), NULL);
else
@ -7219,14 +7251,14 @@ vectorarrayindex:
}
else
{
tmp = QCC_MakeFloatConst(t->params[i].ofs);
tmp = QCC_MakeFloatConst(ofs);
if (idx.cast)
idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], QCC_SupplyConversion(idx, ev_float, true), QCC_SupplyConversion(tmp, ev_float, true), NULL);
else
idx = tmp;
}
arraysize = t->params[i].arraysize;
t = t->params[i].type;
arraysize = p->arraysize;
t = p->type;
if (fld)
t = QCC_PR_FieldType(t);

View file

@ -3657,6 +3657,9 @@ void QCC_PR_Lex (void)
QCC_PR_LexWhitespace (false);
if (currentchunk)
pr_token_line_last = currentchunk->currentlinenumber-1 + pr_token_line;
else
pr_token_line_last = pr_token_line;
pr_token_line = pr_source_line;

View file

@ -2727,6 +2727,8 @@ static LRESULT CALLBACK EditorWndProc(HWND hWnd,UINT message,
{
CHARRANGE chrg;
if (!editor->modified)
editor->oldline=~0;
editor->modified = true;
if (EditorModified(editor))
if (MessageBox(NULL, "warning: file was modified externally. reload?", "Modified!", MB_YESNO) == IDYES)
@ -2835,9 +2837,11 @@ static LRESULT CALLBACK EditorWndProc(HWND hWnd,UINT message,
Scin_HandleCharAdded(editor, not, pos);
break;
case SCN_SAVEPOINTREACHED:
editor->oldline=~0;
editor->modified = false;
break;
case SCN_SAVEPOINTLEFT:
editor->oldline=~0;
editor->modified = true;
if (EditorModified(editor))
@ -3338,6 +3342,7 @@ int EditorSave(editor_t *edit)
edit->filemodifiedtime = sbuf.st_mtime;
//remove the * in a silly way.
edit->oldline=~0;
UpdateEditorTitle(edit);
return true;
@ -5833,8 +5838,23 @@ static LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message,
int i;
RECT rect;
PAINTSTRUCT ps;
editor_t *editor;
switch (message)
{
case WM_CLOSE:
//if any child editors are still open, send close requests to them first.
//this allows them to display prompts, instead of silently losing changes.
for (editor = editors; editor;)
{
editor_t *n = editor->next;
if (editor->window)
SendMessage(editor->window, WM_CLOSE, 0, 0);
editor = n;
}
//okay, they're all dead. we can kill ourselves now.
if (!editors)
DestroyWindow(hWnd);
return 0;
case WM_CREATE:
{
CLIENTCREATESTRUCT ccs;

View file

@ -397,6 +397,7 @@ struct {
} targets[] = {
{QCF_STANDARD, "standard"},
{QCF_STANDARD, "q1"},
{QCF_STANDARD, "id"},
{QCF_STANDARD, "quakec"},
{QCF_HEXEN2, "hexen2"},
{QCF_HEXEN2, "h2"},
@ -4141,6 +4142,8 @@ void QCC_PR_CommandLinePrecompilerOptions (void)
{
flag_acc = true;
}
else if (!strcmp(myargv[i]+5, "frikqcc"))
keyword_state = true;
else if (!strcmp(myargv[i]+5, "fteqcc"))
; //as above, its the default.
else if (!strcmp(myargv[i]+5, "id"))

View file

@ -12733,7 +12733,7 @@ void PR_DumpPlatform_f(void)
"accessor infostring : string\n{\n"
"\tget string[string] = infoget;\n"
#ifdef QCGC
"\tinline set* string[string fld] = {(*this) = infoadd(*this, fld, value);};\n"
"\tinline seti& string[string fld] = {this = infoadd(this, fld, value);};\n"
#endif
"};\n");
VFS_PRINTF(f,

View file

@ -9,6 +9,9 @@ extern cvar_t coop;
extern cvar_t teamplay;
extern cvar_t pr_enable_profiling;
cvar_t sv_savefmt = CVARFD("sv_savefmt", "1", CVAR_SAVE, "Specifies the format used for the saved game.\n0=legacy.\n1=fte\n2=binary");
cvar_t sv_autosave = CVARFD("sv_autosave", "5", CVAR_SAVE, "Interval for autosaves, in minutes. Set to 0 to disable autosave.");
void SV_Savegame_f (void);
//Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current
@ -73,6 +76,7 @@ void SV_SavegameComment (char *text, size_t textsize)
text[textsize-1] = '\0';
}
#ifndef QUAKETC
//expects the version to have already been parsed
void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
{
@ -282,7 +286,7 @@ void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
// the rest of the file is sent directly to the progs engine.
if (version == 5 || version == 6)
Q_InitProgs(); //reinitialize progs entirly.
;//Q_InitProgs(); //reinitialize progs entirly.
else
{
Q_SetProgsParms(false);
@ -360,7 +364,7 @@ void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
}
}
void SV_LegacySavegame_f (void)
static void SV_LegacySavegame (const char *savename)
{
size_t len;
char *s = NULL;
@ -375,18 +379,6 @@ void SV_LegacySavegame_f (void)
int i;
char comment[SAVEGAME_COMMENT_LENGTH+1];
if (Cmd_Argc() != 2)
{
Con_TPrintf ("save <savename> : save a game\n");
return;
}
if (strstr(Cmd_Argv(1), ".."))
{
Con_TPrintf ("Relative pathnames are not allowed\n");
return;
}
if (sv.state != ss_active)
{
Con_TPrintf("Can't apply: Server isn't running or is still loading\n");
@ -401,12 +393,12 @@ void SV_LegacySavegame_f (void)
return;
}
sprintf (name, "%s", Cmd_Argv(1));
sprintf (name, "%s", savename);
COM_RequireExtension (name, ".sav", sizeof(name));
if (!FS_NativePath(name, FS_GAMEONLY, native, sizeof(native)))
return;
Con_TPrintf (U8("Saving game to %s...\n"), native);
f = FS_OpenVFS(name, "wb", FS_GAMEONLY);
f = FS_OpenVFS(name, "wbp", FS_GAMEONLY);
if (!f)
{
Con_TPrintf ("ERROR: couldn't open %s.\n", name);
@ -486,13 +478,16 @@ void SV_LegacySavegame_f (void)
svprogfuncs->parms->memfree(s);
VFS_CLOSE(f);
}
FS_FlushFSHashWritten(name);
}
#endif
#define CACHEGAME_VERSION_OLD 513
#define CACHEGAME_VERSION_VERBOSE 514
#define CACHEGAME_VERSION_BINARY 515
@ -1065,7 +1060,7 @@ void SV_SaveLevelCache(const char *savedir, qboolean dontharmgame)
}
}
if (version == CACHEGAME_VERSION_VERBOSE)
if (version >= CACHEGAME_VERSION_VERBOSE)
{
char buf[8192];
char *mode = "?";
@ -1102,6 +1097,8 @@ void SV_SaveLevelCache(const char *savedir, qboolean dontharmgame)
for (i = 0; i < sizeof(sv.strings.vw_model_precache)/sizeof(sv.strings.vw_model_precache[0]); i++)
VFS_PRINTF (f, "vwep %i %s\n", i, COM_QuotedString(sv.strings.vw_model_precache[i], buf, sizeof(buf), false));
PR_Common_SaveGame(f, svprogfuncs, version >= CACHEGAME_VERSION_BINARY);
//FIXME: string buffers
//FIXME: hash tables
//FIXME: skeletal objects?
@ -1109,9 +1106,10 @@ void SV_SaveLevelCache(const char *savedir, qboolean dontharmgame)
//FIXME: midi track
//FIXME: custom temp-ents?
//FIXME: pending uri_gets? (if only just to report fails)
//FIXME: routing calls?
//FIXME: sql queries?
//FIXME: frik files?
//FIXME: threads?
//FIXME: qc threads?
VFS_PRINTF (f, "entities\n");
}
@ -1150,10 +1148,18 @@ void SV_SaveLevelCache(const char *savedir, qboolean dontharmgame)
VFS_PRINTF (f,"\n");
}
if (version >= CACHEGAME_VERSION_BINARY)
{
VFS_PUTS(f, va("%i\n", svprogfuncs->stringtablesize));
VFS_WRITE(f, svprogfuncs->stringtable, svprogfuncs->stringtablesize);
}
else
{
s = PR_SaveEnts(svprogfuncs, NULL, &len, 0, 1);
VFS_PUTS(f, s);
VFS_PUTS(f, "\n");
svprogfuncs->parms->memfree(s);
}
VFS_CLOSE (f);
@ -1167,11 +1173,12 @@ void SV_SaveLevelCache(const char *savedir, qboolean dontharmgame)
}
}
FS_FlushFSHashFull();
FS_FlushFSHashWritten(name);
}
#define FTESAVEGAME_VERSION 25000
//mapchange is true for Q2's map-change autosaves.
void SV_Savegame (const char *savename, qboolean mapchange)
{
extern cvar_t nomonsters;
@ -1199,6 +1206,14 @@ void SV_Savegame (const char *savename, qboolean mapchange)
char str[MAX_LOCALINFO_STRING+1];
char *savefilename;
#ifndef QUAKETC
if (!sv_savefmt.ival && !mapchange)
{
SV_LegacySavegame(savename);
return;
}
#endif
if (!sv.state || sv.state == ss_clustermode)
{
Con_Printf("Server is not active - unable to save\n");
@ -1240,7 +1255,7 @@ void SV_Savegame (const char *savename, qboolean mapchange)
savefilename = va("saves/%s/info.fsv", savename);
FS_CreatePath(savefilename, FS_GAMEONLY);
f = FS_OpenVFS(savefilename, "wb", FS_GAMEONLY);
f = FS_OpenVFS(savefilename, "wbp", FS_GAMEONLY);
if (!f)
{
Con_Printf("Couldn't open file saves/%s/info.fsv\n", savename);
@ -1417,6 +1432,7 @@ static int QDECL CompleteSaveList (const char *name, qofs_t flags, time_t mtime,
}
return true;
}
#ifndef QUAKETC
static int QDECL CompleteSaveListLegacy (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)
{
struct xcommandargcompletioncb_s *ctx = parm;
@ -1425,24 +1441,39 @@ static int QDECL CompleteSaveListLegacy (const char *name, qofs_t flags, time_t
ctx->cb(stripped, NULL, NULL, ctx);
return true;
}
#endif
void SV_Savegame_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)
{
if (argn == 1)
{
COM_EnumerateFiles(va("saves/%s*/info.fsv", partial), CompleteSaveList, ctx);
#ifndef QUAKETC
COM_EnumerateFiles(va("%s*.sav", partial), CompleteSaveListLegacy, ctx);
#endif
}
}
void SV_Savegame_f (void)
{
if (Cmd_Argc() <= 2)
SV_Savegame(Cmd_Argv(1), false);
{
const char *savename = Cmd_Argv(1);
if (strstr(savename, ".."))
{
Con_TPrintf ("Relative pathnames are not allowed\n");
return;
}
#ifndef QUAKETC
if (!Q_strcasecmp(Cmd_Argv(0), "savegame_legacy"))
SV_LegacySavegame(savename);
else
#endif
SV_Savegame(savename, false);
}
else
Con_Printf("%s: invalid number of arguments\n", Cmd_Argv(0));
}
cvar_t sv_autosave = CVARFD("sv_autosave", "5", CVAR_SAVE, "Interval for autosaves, in minutes. Set to 0 to disable autosave.");
void SV_AutoSave(void)
{
#ifndef NOBUILTINMENUS
@ -1512,21 +1543,44 @@ void SV_Loadgame_f (void)
gametype_e gametype;
int len;
struct
{
char *pattern;
flocation_t loc;
} savefiles[] =
{
{"saves/%s/info.fsv"},
#ifndef QUAKETC
{"%s.sav"},
#endif
};
int bd,best;
#ifndef SERVERONLY
if (!Renderer_Started() && !isDedicated)
{
Cbuf_AddText(va("wait;%s %s\n", Cmd_Argv(0), Cmd_Args()), Cmd_ExecLevel);
return;
}
#endif
Q_strncpyz(savename, Cmd_Argv(1), sizeof(savename));
if (!*savename || strstr(savename, ".."))
strcpy(savename, "quick");
Q_snprintfz (filename, sizeof(filename), "saves/%s/info.fsv", savename);
f = FS_OpenVFS (filename, "rb", FS_GAME);
if (!f)
for (len = 0, bd=0x7fffffff,best=0; len < countof(savefiles); len++)
{
f = FS_OpenVFS (va("%s.sav", savename), "rb", FS_GAME);
if (f)
Q_snprintfz (filename, sizeof(filename), "%s.sav", savename);
int d = FS_FLocateFile(va(savefiles[len].pattern, savename), FSLF_DONTREFERENCE|FSLF_DEEPONFAILURE, &savefiles[len].loc);
if (bd > d)
{
bd = d;
best = len;
}
}
Q_snprintfz (filename, sizeof(filename), savefiles[best].pattern, savename);
f = FS_OpenReadLocation(&savefiles[best].loc);
if (!f)
{
Con_TPrintf ("ERROR: couldn't open %s.\n", filename);
@ -1541,7 +1595,12 @@ void SV_Loadgame_f (void)
version = atoi(str);
if (version < FTESAVEGAME_VERSION || version >= FTESAVEGAME_VERSION+GT_MAX)
{
#ifdef QUAKETC
VFS_CLOSE (f);
Con_TPrintf ("Unable to load savegame of version %i\n", version);
#else
SV_Loadgame_Legacy(filename, f, version);
#endif
return;
}

View file

@ -1581,6 +1581,7 @@ void SV_Loadgame_f (void);
void SV_AutoSave(void);
void SV_FlushLevelCache(void);
extern cvar_t sv_autosave;
extern cvar_t sv_savefmt;
int SV_RateForClient(client_t *cl);

View file

@ -2542,6 +2542,33 @@ static void SV_Gamedir (void)
Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING);
}
static int QDECL CompleteGamedirPath (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)
{
struct xcommandargcompletioncb_s *ctx = parm;
char dirname[MAX_QPATH];
if (*name)
{
size_t l = strlen(name)-1;
if (l < countof(dirname) && name[l] == '/')
{ //directories are marked with an explicit trailing slash. because we're weird.
memcpy(dirname, name, l);
dirname[l] = 0;
ctx->cb(dirname, NULL, NULL, ctx);
}
}
return true;
}
static void SV_Gamedir_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)
{
extern qboolean com_homepathenabled;
if (argn == 1)
{
if (com_homepathenabled)
Sys_EnumerateFiles(com_homepath, va("%s*", partial), CompleteGamedirPath, ctx, NULL);
Sys_EnumerateFiles(com_gamepath, va("%s*", partial), CompleteGamedirPath, ctx, NULL);
}
}
/*
================
SV_Gamedir_f
@ -3045,8 +3072,8 @@ void SV_InitOperatorCommands (void)
Cmd_AddCommand ("heartbeat", SV_Heartbeat_f);
Cmd_AddCommand ("localinfo", SV_Localinfo_f);
Cmd_AddCommandD ("gamedir", SV_Gamedir_f, "Change the current gamedir.");
Cmd_AddCommand ("sv_gamedir", SV_Gamedir);
Cmd_AddCommandAD ("gamedir", SV_Gamedir_f, SV_Gamedir_c, "Change the current gamedir.");
Cmd_AddCommandAD ("sv_gamedir", SV_Gamedir, SV_Gamedir_c, "Change the gamedir reported to clients, without changing any actual paths on the server.");
Cmd_AddCommand ("sv_settimer", SV_SetTimer_f);
Cmd_AddCommand ("stuffcmd", SV_StuffToClient_f);

View file

@ -5213,7 +5213,10 @@ void SV_InitLocal (void)
Cvar_Register(&sv_autosave, cvargroup_servercontrol);
#endif
#endif
Cmd_AddCommandAD ("savegame_legacy", SV_LegacySavegame_f, SV_Savegame_c, "Saves the game in a format compatible with vanilla Quake. Anything not supported by that format will be lost.");
Cvar_Register(&sv_savefmt, cvargroup_servercontrol);
#ifndef QUAKETC
Cmd_AddCommandAD ("savegame_legacy", SV_Savegame_f, SV_Savegame_c, "Saves the game in a format compatible with vanilla Quake. Anything not supported by that format will be lost.");
#endif
Cmd_AddCommandAD ("savegame", SV_Savegame_f, SV_Savegame_c, "Saves the game to the named location.");
Cmd_AddCommandAD ("loadgame", SV_Loadgame_f, SV_Savegame_c, "Loads an existing saved game.");
Cmd_AddCommandAD ("save", SV_Savegame_f, SV_Savegame_c, "Saves the game to the named location.");