2011-02-18 14:31:32 +00:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
|
|
|
|
Quake III Arena source code 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.
|
|
|
|
|
|
|
|
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
// vm.c -- virtual machine
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
|
|
intermix code and data
|
|
|
|
symbol table
|
|
|
|
|
|
|
|
a dll has one imported function: VM_SystemCall
|
|
|
|
and one exported function: Perform
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "vm_local.h"
|
|
|
|
|
|
|
|
|
|
|
|
vm_t *currentVM = NULL;
|
|
|
|
vm_t *lastVM = NULL;
|
|
|
|
int vm_debugLevel;
|
|
|
|
|
|
|
|
// used by Com_Error to get rid of running vm's before longjmp
|
|
|
|
static int forced_unload;
|
|
|
|
|
|
|
|
#define MAX_VM 3
|
|
|
|
vm_t vmTable[MAX_VM];
|
|
|
|
|
|
|
|
|
|
|
|
void VM_VmInfo_f( void );
|
|
|
|
void VM_VmProfile_f( void );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if 0 // 64bit!
|
|
|
|
// converts a VM pointer to a C pointer and
|
|
|
|
// checks to make sure that the range is acceptable
|
|
|
|
void *VM_VM2C( vmptr_t p, int length ) {
|
|
|
|
return (void *)p;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void VM_Debug( int level ) {
|
|
|
|
vm_debugLevel = level;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
VM_Init
|
|
|
|
==============
|
|
|
|
*/
|
|
|
|
void VM_Init( void ) {
|
|
|
|
Cvar_Get( "vm_cgame", "2", CVAR_ARCHIVE ); // !@# SHIP WITH SET TO 2
|
|
|
|
Cvar_Get( "vm_game", "2", CVAR_ARCHIVE ); // !@# SHIP WITH SET TO 2
|
|
|
|
Cvar_Get( "vm_ui", "2", CVAR_ARCHIVE ); // !@# SHIP WITH SET TO 2
|
|
|
|
|
|
|
|
Cmd_AddCommand ("vmprofile", VM_VmProfile_f );
|
|
|
|
Cmd_AddCommand ("vminfo", VM_VmInfo_f );
|
|
|
|
|
|
|
|
Com_Memset( vmTable, 0, sizeof( vmTable ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
VM_ValueToSymbol
|
|
|
|
|
|
|
|
Assumes a program counter value
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
const char *VM_ValueToSymbol( vm_t *vm, int value ) {
|
|
|
|
vmSymbol_t *sym;
|
|
|
|
static char text[MAX_TOKEN_CHARS];
|
|
|
|
|
|
|
|
sym = vm->symbols;
|
|
|
|
if ( !sym ) {
|
|
|
|
return "NO SYMBOLS";
|
|
|
|
}
|
|
|
|
|
|
|
|
// find the symbol
|
|
|
|
while ( sym->next && sym->next->symValue <= value ) {
|
|
|
|
sym = sym->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( value == sym->symValue ) {
|
|
|
|
return sym->symName;
|
|
|
|
}
|
|
|
|
|
|
|
|
Com_sprintf( text, sizeof( text ), "%s+%i", sym->symName, value - sym->symValue );
|
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
VM_ValueToFunctionSymbol
|
|
|
|
|
|
|
|
For profiling, find the symbol behind this value
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ) {
|
|
|
|
vmSymbol_t *sym;
|
|
|
|
static vmSymbol_t nullSym;
|
|
|
|
|
|
|
|
sym = vm->symbols;
|
|
|
|
if ( !sym ) {
|
|
|
|
return &nullSym;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ( sym->next && sym->next->symValue <= value ) {
|
|
|
|
sym = sym->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sym;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
VM_SymbolToValue
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
int VM_SymbolToValue( vm_t *vm, const char *symbol ) {
|
|
|
|
vmSymbol_t *sym;
|
|
|
|
|
|
|
|
for ( sym = vm->symbols ; sym ; sym = sym->next ) {
|
|
|
|
if ( !strcmp( symbol, sym->symName ) ) {
|
|
|
|
return sym->symValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
=====================
|
|
|
|
VM_SymbolForCompiledPointer
|
|
|
|
=====================
|
|
|
|
*/
|
|
|
|
#if 0 // 64bit!
|
|
|
|
const char *VM_SymbolForCompiledPointer( vm_t *vm, void *code ) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ( code < (void *)vm->codeBase ) {
|
|
|
|
return "Before code block";
|
|
|
|
}
|
|
|
|
if ( code >= (void *)(vm->codeBase + vm->codeLength) ) {
|
|
|
|
return "After code block";
|
|
|
|
}
|
|
|
|
|
|
|
|
// find which original instruction it is after
|
|
|
|
for ( i = 0 ; i < vm->codeLength ; i++ ) {
|
|
|
|
if ( (void *)vm->instructionPointers[i] > code ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i--;
|
|
|
|
|
|
|
|
// now look up the bytecode instruction pointer
|
|
|
|
return VM_ValueToSymbol( vm, i );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
ParseHex
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
int ParseHex( const char *text ) {
|
|
|
|
int value;
|
|
|
|
int c;
|
|
|
|
|
|
|
|
value = 0;
|
|
|
|
while ( ( c = *text++ ) != 0 ) {
|
|
|
|
if ( c >= '0' && c <= '9' ) {
|
|
|
|
value = value * 16 + c - '0';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ( c >= 'a' && c <= 'f' ) {
|
|
|
|
value = value * 16 + 10 + c - 'a';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ( c >= 'A' && c <= 'F' ) {
|
|
|
|
value = value * 16 + 10 + c - 'A';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
VM_LoadSymbols
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
void VM_LoadSymbols( vm_t *vm ) {
|
|
|
|
union {
|
|
|
|
char *c;
|
|
|
|
void *v;
|
|
|
|
} mapfile;
|
|
|
|
char *text_p, *token;
|
|
|
|
char name[MAX_QPATH];
|
|
|
|
char symbols[MAX_QPATH];
|
|
|
|
vmSymbol_t **prev, *sym;
|
|
|
|
int count;
|
|
|
|
int value;
|
|
|
|
int chars;
|
|
|
|
int segment;
|
|
|
|
int numInstructions;
|
|
|
|
|
|
|
|
// don't load symbols if not developer
|
|
|
|
if ( !com_developer->integer ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
COM_StripExtension(vm->name, name, sizeof(name));
|
|
|
|
Com_sprintf( symbols, sizeof( symbols ), "vm/%s.map", name );
|
2011-07-29 17:52:36 +00:00
|
|
|
FS_ReadFile( symbols, &mapfile.v );
|
2011-02-18 14:31:32 +00:00
|
|
|
if ( !mapfile.c ) {
|
|
|
|
Com_Printf( "Couldn't load symbol file: %s\n", symbols );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
numInstructions = vm->instructionCount;
|
|
|
|
|
|
|
|
// parse the symbols
|
|
|
|
text_p = mapfile.c;
|
|
|
|
prev = &vm->symbols;
|
|
|
|
count = 0;
|
|
|
|
|
|
|
|
while ( 1 ) {
|
|
|
|
token = COM_Parse( &text_p );
|
|
|
|
if ( !token[0] ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
segment = ParseHex( token );
|
|
|
|
if ( segment ) {
|
|
|
|
COM_Parse( &text_p );
|
|
|
|
COM_Parse( &text_p );
|
|
|
|
continue; // only load code segment values
|
|
|
|
}
|
|
|
|
|
|
|
|
token = COM_Parse( &text_p );
|
|
|
|
if ( !token[0] ) {
|
|
|
|
Com_Printf( "WARNING: incomplete line at end of file\n" );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
value = ParseHex( token );
|
|
|
|
|
|
|
|
token = COM_Parse( &text_p );
|
|
|
|
if ( !token[0] ) {
|
|
|
|
Com_Printf( "WARNING: incomplete line at end of file\n" );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
chars = strlen( token );
|
|
|
|
sym = Hunk_Alloc( sizeof( *sym ) + chars, h_high );
|
|
|
|
*prev = sym;
|
|
|
|
prev = &sym->next;
|
|
|
|
sym->next = NULL;
|
|
|
|
|
|
|
|
// convert value from an instruction number to a code offset
|
|
|
|
if ( value >= 0 && value < numInstructions ) {
|
|
|
|
value = vm->instructionPointers[value];
|
|
|
|
}
|
|
|
|
|
|
|
|
sym->symValue = value;
|
|
|
|
Q_strncpyz( sym->symName, token, chars + 1 );
|
|
|
|
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
vm->numSymbols = count;
|
|
|
|
Com_Printf( "%i symbols parsed from %s\n", count, symbols );
|
|
|
|
FS_FreeFile( mapfile.v );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
============
|
|
|
|
VM_DllSyscall
|
|
|
|
|
|
|
|
Dlls will call this directly
|
|
|
|
|
|
|
|
rcg010206 The horror; the horror.
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
The syscall mechanism relies on stack manipulation to get its args.
|
2011-02-18 14:31:32 +00:00
|
|
|
This is likely due to C's inability to pass "..." parameters to
|
|
|
|
a function in one clean chunk. On PowerPC Linux, these parameters
|
|
|
|
are not necessarily passed on the stack, so while (&arg[0] == arg)
|
|
|
|
is true, (&arg[1] == 2nd function parameter) is not necessarily
|
|
|
|
accurate, as arg's value might have been stored to the stack or
|
|
|
|
other piece of scratch memory to give it a valid address, but the
|
|
|
|
next parameter might still be sitting in a register.
|
|
|
|
|
|
|
|
Quake's syscall system also assumes that the stack grows downward,
|
|
|
|
and that any needed types can be squeezed, safely, into a signed int.
|
|
|
|
|
|
|
|
This hack below copies all needed values for an argument to a
|
|
|
|
array in memory, so that Quake can get the correct values. This can
|
|
|
|
also be used on systems where the stack grows upwards, as the
|
|
|
|
presumably standard and safe stdargs.h macros are used.
|
|
|
|
|
|
|
|
As for having enough space in a signed int for your datatypes, well,
|
|
|
|
it might be better to wait for DOOM 3 before you start porting. :)
|
|
|
|
|
|
|
|
The original code, while probably still inherently dangerous, seems
|
|
|
|
to work well enough for the platforms it already works on. Rather
|
|
|
|
than add the performance hit for those platforms, the original code
|
|
|
|
is still in use there.
|
|
|
|
|
|
|
|
For speed, we just grab 15 arguments, and don't worry about exactly
|
|
|
|
how many the syscall actually needs; the extra is thrown away.
|
|
|
|
|
|
|
|
============
|
|
|
|
*/
|
|
|
|
intptr_t QDECL VM_DllSyscall( intptr_t arg, ... ) {
|
2012-09-15 03:03:44 +00:00
|
|
|
#if !id386 || defined __clang__
|
2011-02-18 14:31:32 +00:00
|
|
|
// rcg010206 - see commentary above
|
ioquake3 resync to revision 2398 from 2369.
This is the last ioquake3 revision before ioquake3 changed from subversion to git at the beginning of 2013.
#5808 - Include and use .glsl in source (rend2)
#5812 - Use refdef's coordinates when drawing to screen shadow fbo, and separate depth texture and screen texture coordinates in glsl shaders.
Include Rend2 renderer in MacOSX bundle
Include OpenGL1 and Rend2 renderers in MacOSX UB
Include Rend2 renderer in NSIS installer.
Include OpenGL1 and Rend2 renderers in Loki Setup Installer.
Have NSIS uninstaller delete rend2.
Split light sample into direct and ambient parts when using deluxemaps or per-vertex light vectors. Fixes #5813.
Fix writting voip data in demos (broke in r2102).
Fix server ignoring client move commands if voip data is included.
Allow changing cl_voip without restarting.
Fix assert failing in CL_ParseVoip() while flipping cl_voip off and on.
Only declare var_SampleToView in lightall shader when it is actually used.
Fix a couple files not ending with a newline.
Fix clients being able to reset their player state and respawn using donedl.
Fix passing arg9 (qvm only), arg10, and arg11 to vmMain for native libs and non-i386 compiled or interpated qvms. (Currently they aren't use in vmMain in game, cgame, or ui.)
Fix passing args[11] to args[15] from vm to engine on ppc64 and sparc64. Some of the args are used by game bot prediction syscalls. May have been causing bugs. Note: This was fixed for x86_64 in r2163.
Fix reconnect command to work after leaving server. (#5794)
Fix dedicated server crashing when using MSG_ReadDelta*, though it only happens if someone modifies the engine. (#5449)
Makefile fixes for OpenBSD by Jonathan Gray. (#5728)
Save all arguments from connect for reconnect command.
Remove unnecessary localhost check from reconnect command.
Support r_srgb even without hardware support. Also tweak default autoexposure/tonemap settings to look good on both r_srgb 0 and 1.
Changed the MacOS-X build system to make UB's containing i386 and x86_64 arches and made make-macosx.sh not build UB's but only standard binaries
Fix spectator client being switched from follow to free after map_restart if following a client with a higher client number.
Fix client unlinking issue caused by ent->s.number being set to followed client's ps->clientNum after map_restart. Reported by Ensiform.
Changes from Ensiform:
- In G_AddBot, try to allocate clientNum before doing anything else.
- In G_AddBot, don't set SVF_BOT and inuse. It's done in ClientConnect, plus inuse causes ClientDisconnect to be run for no reason.
- In G_AddBot, only set skill in bot useinfo once.
- Avoid using cl->ps.clientNum to check if cl is a bot.
Fix bot skill format so it doesn't always have a space at the beginning of it.
More fixes to the macosx buildsystem. This removes the SDL Framework and makes use of a SDL library that is position independant. This also brings back PPC builds into the UB and also as a standa alone build choice.
Have make-macosx.sh require the user to specify which architecture she/he wants to build for and suggest building UB's if the user is unaware of what architectures are
Lets list all the valid options.
2017-07-09 21:21:12 +00:00
|
|
|
intptr_t args[MAX_VMSYSCALL_ARGS];
|
2011-02-18 14:31:32 +00:00
|
|
|
int i;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
args[0] = arg;
|
|
|
|
|
|
|
|
va_start(ap, arg);
|
2011-07-26 08:52:24 +00:00
|
|
|
for (i = 1; i < ARRAY_LEN (args); i++)
|
2011-02-18 14:31:32 +00:00
|
|
|
args[i] = va_arg(ap, intptr_t);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return currentVM->systemCall( args );
|
|
|
|
#else // original id code
|
|
|
|
return currentVM->systemCall( &arg );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
VM_LoadQVM
|
|
|
|
|
|
|
|
Load a .qvm file
|
|
|
|
=================
|
|
|
|
*/
|
2011-12-10 00:15:42 +00:00
|
|
|
vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc, qboolean unpure)
|
|
|
|
{
|
2011-02-18 14:31:32 +00:00
|
|
|
int dataLength;
|
|
|
|
int i;
|
|
|
|
char filename[MAX_QPATH];
|
|
|
|
union {
|
|
|
|
vmHeader_t *h;
|
|
|
|
void *v;
|
|
|
|
} header;
|
|
|
|
|
|
|
|
// load the image
|
|
|
|
Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name );
|
|
|
|
Com_Printf( "Loading vm file %s...\n", filename );
|
2011-07-26 08:52:24 +00:00
|
|
|
|
2011-12-10 00:15:42 +00:00
|
|
|
FS_ReadFileDir(filename, vm->searchPath, unpure, &header.v);
|
2011-07-26 08:52:24 +00:00
|
|
|
|
2011-02-18 14:31:32 +00:00
|
|
|
if ( !header.h ) {
|
|
|
|
Com_Printf( "Failed.\n" );
|
|
|
|
VM_Free( vm );
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
Com_Printf(S_COLOR_YELLOW "Warning: Couldn't open VM file %s\n", filename);
|
|
|
|
|
2011-02-18 14:31:32 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
// show where the qvm was loaded from
|
|
|
|
FS_Which(filename, vm->searchPath);
|
|
|
|
|
2011-02-18 14:31:32 +00:00
|
|
|
if( LittleLong( header.h->vmMagic ) == VM_MAGIC_VER2 ) {
|
|
|
|
Com_Printf( "...which has vmMagic VM_MAGIC_VER2\n" );
|
|
|
|
|
|
|
|
// byte swap the header
|
|
|
|
for ( i = 0 ; i < sizeof( vmHeader_t ) / 4 ; i++ ) {
|
|
|
|
((int *)header.h)[i] = LittleLong( ((int *)header.h)[i] );
|
|
|
|
}
|
|
|
|
|
|
|
|
// validate
|
|
|
|
if ( header.h->jtrgLength < 0
|
|
|
|
|| header.h->bssLength < 0
|
|
|
|
|| header.h->dataLength < 0
|
|
|
|
|| header.h->litLength < 0
|
2011-07-26 08:52:24 +00:00
|
|
|
|| header.h->codeLength <= 0 )
|
|
|
|
{
|
|
|
|
VM_Free(vm);
|
|
|
|
FS_FreeFile(header.v);
|
|
|
|
|
|
|
|
Com_Printf(S_COLOR_YELLOW "Warning: %s has bad header\n", filename);
|
|
|
|
return NULL;
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
} else if( LittleLong( header.h->vmMagic ) == VM_MAGIC ) {
|
|
|
|
// byte swap the header
|
|
|
|
// sizeof( vmHeader_t ) - sizeof( int ) is the 1.32b vm header size
|
|
|
|
for ( i = 0 ; i < ( sizeof( vmHeader_t ) - sizeof( int ) ) / 4 ; i++ ) {
|
|
|
|
((int *)header.h)[i] = LittleLong( ((int *)header.h)[i] );
|
|
|
|
}
|
|
|
|
|
|
|
|
// validate
|
|
|
|
if ( header.h->bssLength < 0
|
|
|
|
|| header.h->dataLength < 0
|
|
|
|
|| header.h->litLength < 0
|
2011-07-26 08:52:24 +00:00
|
|
|
|| header.h->codeLength <= 0 )
|
|
|
|
{
|
|
|
|
VM_Free(vm);
|
|
|
|
FS_FreeFile(header.v);
|
|
|
|
|
|
|
|
Com_Printf(S_COLOR_YELLOW "Warning: %s has bad header\n", filename);
|
|
|
|
return NULL;
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
VM_Free( vm );
|
2011-07-26 08:52:24 +00:00
|
|
|
FS_FreeFile(header.v);
|
|
|
|
|
|
|
|
Com_Printf(S_COLOR_YELLOW "Warning: %s does not have a recognisable "
|
|
|
|
"magic number in its header\n", filename);
|
|
|
|
return NULL;
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// round up to next power of 2 so all data operations can
|
|
|
|
// be mask protected
|
|
|
|
dataLength = header.h->dataLength + header.h->litLength +
|
|
|
|
header.h->bssLength;
|
|
|
|
for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) {
|
|
|
|
}
|
|
|
|
dataLength = 1 << i;
|
|
|
|
|
2011-12-10 00:15:42 +00:00
|
|
|
if(alloc)
|
|
|
|
{
|
2011-02-18 14:31:32 +00:00
|
|
|
// allocate zero filled space for initialized and uninitialized data
|
2017-07-10 01:33:41 +00:00
|
|
|
// leave some space beyond data mask so we can secure all mask operations
|
|
|
|
vm->dataAlloc = dataLength + 4;
|
|
|
|
vm->dataBase = Hunk_Alloc(vm->dataAlloc, h_high);
|
2011-02-18 14:31:32 +00:00
|
|
|
vm->dataMask = dataLength - 1;
|
2011-12-10 00:15:42 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// clear the data, but make sure we're not clearing more than allocated
|
2017-07-10 01:33:41 +00:00
|
|
|
if(vm->dataAlloc != dataLength + 4)
|
2011-12-10 00:15:42 +00:00
|
|
|
{
|
|
|
|
VM_Free(vm);
|
|
|
|
FS_FreeFile(header.v);
|
|
|
|
|
|
|
|
Com_Printf(S_COLOR_YELLOW "Warning: Data region size of %s not matching after "
|
|
|
|
"VM_Restart()\n", filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-07-10 01:33:41 +00:00
|
|
|
Com_Memset(vm->dataBase, 0, vm->dataAlloc);
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// copy the intialized data
|
|
|
|
Com_Memcpy( vm->dataBase, (byte *)header.h + header.h->dataOffset,
|
|
|
|
header.h->dataLength + header.h->litLength );
|
|
|
|
|
|
|
|
// byte swap the longs
|
|
|
|
for ( i = 0 ; i < header.h->dataLength ; i += 4 ) {
|
|
|
|
*(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) );
|
|
|
|
}
|
|
|
|
|
2011-12-10 00:15:42 +00:00
|
|
|
if(header.h->vmMagic == VM_MAGIC_VER2)
|
|
|
|
{
|
|
|
|
int previousNumJumpTableTargets = vm->numJumpTableTargets;
|
|
|
|
|
|
|
|
header.h->jtrgLength &= ~0x03;
|
|
|
|
|
2011-02-18 14:31:32 +00:00
|
|
|
vm->numJumpTableTargets = header.h->jtrgLength >> 2;
|
2011-12-10 00:15:42 +00:00
|
|
|
Com_Printf("Loading %d jump table targets\n", vm->numJumpTableTargets);
|
2011-02-18 14:31:32 +00:00
|
|
|
|
2011-12-10 00:15:42 +00:00
|
|
|
if(alloc)
|
|
|
|
{
|
|
|
|
vm->jumpTableTargets = Hunk_Alloc(header.h->jtrgLength, h_high);
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
2011-12-10 00:15:42 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if(vm->numJumpTableTargets != previousNumJumpTableTargets)
|
|
|
|
{
|
|
|
|
VM_Free(vm);
|
|
|
|
FS_FreeFile(header.v);
|
2011-02-18 14:31:32 +00:00
|
|
|
|
2011-12-10 00:15:42 +00:00
|
|
|
Com_Printf(S_COLOR_YELLOW "Warning: Jump table size of %s not matching after "
|
|
|
|
"VM_Restart()\n", filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Com_Memset(vm->jumpTableTargets, 0, header.h->jtrgLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
Com_Memcpy(vm->jumpTableTargets, (byte *) header.h + header.h->dataOffset +
|
|
|
|
header.h->dataLength + header.h->litLength, header.h->jtrgLength);
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
// byte swap the longs
|
|
|
|
for ( i = 0 ; i < header.h->jtrgLength ; i += 4 ) {
|
|
|
|
*(int *)(vm->jumpTableTargets + i) = LittleLong( *(int *)(vm->jumpTableTargets + i ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return header.h;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
VM_Restart
|
|
|
|
|
|
|
|
Reload the data, but leave everything else in place
|
|
|
|
This allows a server to do a map_restart without changing memory allocation
|
2011-12-10 00:15:42 +00:00
|
|
|
|
|
|
|
We need to make sure that servers can access unpure QVMs (not contained in any pak)
|
|
|
|
even if the client is pure, so take "unpure" as argument.
|
2011-02-18 14:31:32 +00:00
|
|
|
=================
|
|
|
|
*/
|
2011-12-10 00:15:42 +00:00
|
|
|
vm_t *VM_Restart(vm_t *vm, qboolean unpure)
|
|
|
|
{
|
2011-02-18 14:31:32 +00:00
|
|
|
vmHeader_t *header;
|
|
|
|
|
|
|
|
// DLL's can't be restarted in place
|
|
|
|
if ( vm->dllHandle ) {
|
|
|
|
char name[MAX_QPATH];
|
|
|
|
intptr_t (*systemCall)( intptr_t *parms );
|
|
|
|
|
|
|
|
systemCall = vm->systemCall;
|
|
|
|
Q_strncpyz( name, vm->name, sizeof( name ) );
|
|
|
|
|
|
|
|
VM_Free( vm );
|
|
|
|
|
|
|
|
vm = VM_Create( name, systemCall, VMI_NATIVE );
|
|
|
|
return vm;
|
|
|
|
}
|
|
|
|
|
|
|
|
// load the image
|
2011-12-10 00:15:42 +00:00
|
|
|
Com_Printf("VM_Restart()\n");
|
2011-02-18 14:31:32 +00:00
|
|
|
|
2011-12-10 00:15:42 +00:00
|
|
|
if(!(header = VM_LoadQVM(vm, qfalse, unpure)))
|
|
|
|
{
|
|
|
|
Com_Error(ERR_DROP, "VM_Restart failed");
|
2011-02-18 14:31:32 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// free the original file
|
2011-12-10 00:15:42 +00:00
|
|
|
FS_FreeFile(header);
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
return vm;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
================
|
|
|
|
VM_Create
|
|
|
|
|
|
|
|
If image ends in .qvm it will be interpreted, otherwise
|
|
|
|
it will attempt to load as a system dll
|
|
|
|
================
|
|
|
|
*/
|
|
|
|
vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *),
|
|
|
|
vmInterpret_t interpret ) {
|
|
|
|
vm_t *vm;
|
|
|
|
vmHeader_t *header;
|
2011-07-26 08:52:24 +00:00
|
|
|
int i, remaining, retval;
|
|
|
|
char filename[MAX_OSPATH];
|
|
|
|
void *startSearch = NULL;
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
if ( !module || !module[0] || !systemCalls ) {
|
|
|
|
Com_Error( ERR_FATAL, "VM_Create: bad parms" );
|
|
|
|
}
|
|
|
|
|
|
|
|
remaining = Hunk_MemoryRemaining();
|
|
|
|
|
|
|
|
// see if we already have the VM
|
|
|
|
for ( i = 0 ; i < MAX_VM ; i++ ) {
|
|
|
|
if (!Q_stricmp(vmTable[i].name, module)) {
|
|
|
|
vm = &vmTable[i];
|
|
|
|
return vm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// find a free vm
|
|
|
|
for ( i = 0 ; i < MAX_VM ; i++ ) {
|
|
|
|
if ( !vmTable[i].name[0] ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( i == MAX_VM ) {
|
|
|
|
Com_Error( ERR_FATAL, "VM_Create: no free vm_t" );
|
|
|
|
}
|
|
|
|
|
|
|
|
vm = &vmTable[i];
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
Q_strncpyz(vm->name, module, sizeof(vm->name));
|
2011-02-18 14:31:32 +00:00
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
do
|
|
|
|
{
|
|
|
|
retval = FS_FindVM(&startSearch, filename, sizeof(filename), module, (interpret == VMI_NATIVE));
|
|
|
|
|
|
|
|
if(retval == VMI_NATIVE)
|
|
|
|
{
|
|
|
|
Com_Printf("Try loading dll file %s\n", filename);
|
|
|
|
|
2011-08-10 22:10:14 +00:00
|
|
|
vm->dllHandle = Sys_LoadGameDll(filename, &vm->entryPoint, VM_DllSyscall);
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
if(vm->dllHandle)
|
|
|
|
{
|
|
|
|
vm->systemCall = systemCalls;
|
|
|
|
return vm;
|
|
|
|
}
|
|
|
|
|
|
|
|
Com_Printf("Failed loading dll, trying next\n");
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
2011-07-26 08:52:24 +00:00
|
|
|
else if(retval == VMI_COMPILED)
|
|
|
|
{
|
|
|
|
vm->searchPath = startSearch;
|
2011-12-10 00:15:42 +00:00
|
|
|
if((header = VM_LoadQVM(vm, qtrue, qfalse)))
|
2011-07-26 08:52:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// VM_Free overwrites the name on failed load
|
|
|
|
Q_strncpyz(vm->name, module, sizeof(vm->name));
|
|
|
|
}
|
|
|
|
} while(retval >= 0);
|
|
|
|
|
|
|
|
if(retval < 0)
|
2011-02-18 14:31:32 +00:00
|
|
|
return NULL;
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
vm->systemCall = systemCalls;
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
// allocate space for the jump targets, which will be filled in by the compile/prep functions
|
|
|
|
vm->instructionCount = header->instructionCount;
|
2011-07-26 08:52:24 +00:00
|
|
|
vm->instructionPointers = Hunk_Alloc(vm->instructionCount * sizeof(*vm->instructionPointers), h_high);
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
// copy or compile the instructions
|
|
|
|
vm->codeLength = header->codeLength;
|
|
|
|
|
|
|
|
vm->compiled = qfalse;
|
|
|
|
|
|
|
|
#ifdef NO_VM_COMPILED
|
|
|
|
if(interpret >= VMI_COMPILED) {
|
|
|
|
Com_Printf("Architecture doesn't have a bytecode compiler, using interpreter\n");
|
|
|
|
interpret = VMI_BYTECODE;
|
|
|
|
}
|
|
|
|
#else
|
2011-07-26 08:52:24 +00:00
|
|
|
if(interpret != VMI_BYTECODE)
|
|
|
|
{
|
2011-02-18 14:31:32 +00:00
|
|
|
vm->compiled = qtrue;
|
|
|
|
VM_Compile( vm, header );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// VM_Compile may have reset vm->compiled if compilation failed
|
|
|
|
if (!vm->compiled)
|
|
|
|
{
|
|
|
|
VM_PrepareInterpreter( vm, header );
|
|
|
|
}
|
|
|
|
|
|
|
|
// free the original file
|
|
|
|
FS_FreeFile( header );
|
|
|
|
|
|
|
|
// load the map file
|
|
|
|
VM_LoadSymbols( vm );
|
|
|
|
|
|
|
|
// the stack is implicitly at the end of the image
|
|
|
|
vm->programStack = vm->dataMask + 1;
|
|
|
|
vm->stackBottom = vm->programStack - PROGRAM_STACK_SIZE;
|
|
|
|
|
|
|
|
Com_Printf("%s loaded in %d bytes on the hunk\n", module, remaining - Hunk_MemoryRemaining());
|
|
|
|
|
|
|
|
return vm;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
VM_Free
|
|
|
|
==============
|
|
|
|
*/
|
|
|
|
void VM_Free( vm_t *vm ) {
|
|
|
|
|
|
|
|
if(!vm) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(vm->callLevel) {
|
|
|
|
if(!forced_unload) {
|
|
|
|
Com_Error( ERR_FATAL, "VM_Free(%s) on running vm", vm->name );
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
Com_Printf( "forcefully unloading %s vm\n", vm->name );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(vm->destroy)
|
|
|
|
vm->destroy(vm);
|
|
|
|
|
|
|
|
if ( vm->dllHandle ) {
|
|
|
|
Sys_UnloadDll( vm->dllHandle );
|
|
|
|
Com_Memset( vm, 0, sizeof( *vm ) );
|
|
|
|
}
|
|
|
|
#if 0 // now automatically freed by hunk
|
|
|
|
if ( vm->codeBase ) {
|
|
|
|
Z_Free( vm->codeBase );
|
|
|
|
}
|
|
|
|
if ( vm->dataBase ) {
|
|
|
|
Z_Free( vm->dataBase );
|
|
|
|
}
|
|
|
|
if ( vm->instructionPointers ) {
|
|
|
|
Z_Free( vm->instructionPointers );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
Com_Memset( vm, 0, sizeof( *vm ) );
|
|
|
|
|
|
|
|
currentVM = NULL;
|
|
|
|
lastVM = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VM_Clear(void) {
|
|
|
|
int i;
|
|
|
|
for (i=0;i<MAX_VM; i++) {
|
|
|
|
VM_Free(&vmTable[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VM_Forced_Unload_Start(void) {
|
|
|
|
forced_unload = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VM_Forced_Unload_Done(void) {
|
|
|
|
forced_unload = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *VM_ArgPtr( intptr_t intValue ) {
|
|
|
|
if ( !intValue ) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
// currentVM is missing on reconnect
|
|
|
|
if ( currentVM==NULL )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if ( currentVM->entryPoint ) {
|
|
|
|
return (void *)(currentVM->dataBase + intValue);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return (void *)(currentVM->dataBase + (intValue & currentVM->dataMask));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void *VM_ExplicitArgPtr( vm_t *vm, intptr_t intValue ) {
|
|
|
|
if ( !intValue ) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// currentVM is missing on reconnect here as well?
|
|
|
|
if ( currentVM==NULL )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
//
|
|
|
|
if ( vm->entryPoint ) {
|
|
|
|
return (void *)(vm->dataBase + intValue);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return (void *)(vm->dataBase + (intValue & vm->dataMask));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
VM_Call
|
|
|
|
|
|
|
|
|
|
|
|
Upon a system call, the stack will look like:
|
|
|
|
|
|
|
|
sp+32 parm1
|
|
|
|
sp+28 parm0
|
|
|
|
sp+24 return value
|
|
|
|
sp+20 return address
|
|
|
|
sp+16 local1
|
|
|
|
sp+14 local0
|
|
|
|
sp+12 arg1
|
|
|
|
sp+8 arg0
|
|
|
|
sp+4 return stack
|
|
|
|
sp return address
|
|
|
|
|
|
|
|
An interpreted function will immediately execute
|
|
|
|
an OP_ENTER instruction, which will subtract space for
|
|
|
|
locals from sp
|
|
|
|
==============
|
|
|
|
*/
|
|
|
|
|
2011-12-10 00:15:42 +00:00
|
|
|
intptr_t QDECL VM_Call( vm_t *vm, int callnum, ... )
|
|
|
|
{
|
2011-02-18 14:31:32 +00:00
|
|
|
vm_t *oldVM;
|
|
|
|
intptr_t r;
|
|
|
|
int i;
|
|
|
|
|
2011-12-10 00:15:42 +00:00
|
|
|
if(!vm || !vm->name[0])
|
|
|
|
Com_Error(ERR_FATAL, "VM_Call with NULL vm");
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
oldVM = currentVM;
|
|
|
|
currentVM = vm;
|
|
|
|
lastVM = vm;
|
|
|
|
|
|
|
|
if ( vm_debugLevel ) {
|
|
|
|
Com_Printf( "VM_Call( %d )\n", callnum );
|
|
|
|
}
|
|
|
|
|
|
|
|
++vm->callLevel;
|
|
|
|
// if we have a dll loaded, call it directly
|
|
|
|
if ( vm->entryPoint ) {
|
|
|
|
//rcg010207 - see dissertation at top of VM_DllSyscall() in this file.
|
ioquake3 resync to revision 2398 from 2369.
This is the last ioquake3 revision before ioquake3 changed from subversion to git at the beginning of 2013.
#5808 - Include and use .glsl in source (rend2)
#5812 - Use refdef's coordinates when drawing to screen shadow fbo, and separate depth texture and screen texture coordinates in glsl shaders.
Include Rend2 renderer in MacOSX bundle
Include OpenGL1 and Rend2 renderers in MacOSX UB
Include Rend2 renderer in NSIS installer.
Include OpenGL1 and Rend2 renderers in Loki Setup Installer.
Have NSIS uninstaller delete rend2.
Split light sample into direct and ambient parts when using deluxemaps or per-vertex light vectors. Fixes #5813.
Fix writting voip data in demos (broke in r2102).
Fix server ignoring client move commands if voip data is included.
Allow changing cl_voip without restarting.
Fix assert failing in CL_ParseVoip() while flipping cl_voip off and on.
Only declare var_SampleToView in lightall shader when it is actually used.
Fix a couple files not ending with a newline.
Fix clients being able to reset their player state and respawn using donedl.
Fix passing arg9 (qvm only), arg10, and arg11 to vmMain for native libs and non-i386 compiled or interpated qvms. (Currently they aren't use in vmMain in game, cgame, or ui.)
Fix passing args[11] to args[15] from vm to engine on ppc64 and sparc64. Some of the args are used by game bot prediction syscalls. May have been causing bugs. Note: This was fixed for x86_64 in r2163.
Fix reconnect command to work after leaving server. (#5794)
Fix dedicated server crashing when using MSG_ReadDelta*, though it only happens if someone modifies the engine. (#5449)
Makefile fixes for OpenBSD by Jonathan Gray. (#5728)
Save all arguments from connect for reconnect command.
Remove unnecessary localhost check from reconnect command.
Support r_srgb even without hardware support. Also tweak default autoexposure/tonemap settings to look good on both r_srgb 0 and 1.
Changed the MacOS-X build system to make UB's containing i386 and x86_64 arches and made make-macosx.sh not build UB's but only standard binaries
Fix spectator client being switched from follow to free after map_restart if following a client with a higher client number.
Fix client unlinking issue caused by ent->s.number being set to followed client's ps->clientNum after map_restart. Reported by Ensiform.
Changes from Ensiform:
- In G_AddBot, try to allocate clientNum before doing anything else.
- In G_AddBot, don't set SVF_BOT and inuse. It's done in ClientConnect, plus inuse causes ClientDisconnect to be run for no reason.
- In G_AddBot, only set skill in bot useinfo once.
- Avoid using cl->ps.clientNum to check if cl is a bot.
Fix bot skill format so it doesn't always have a space at the beginning of it.
More fixes to the macosx buildsystem. This removes the SDL Framework and makes use of a SDL library that is position independant. This also brings back PPC builds into the UB and also as a standa alone build choice.
Have make-macosx.sh require the user to specify which architecture she/he wants to build for and suggest building UB's if the user is unaware of what architectures are
Lets list all the valid options.
2017-07-09 21:21:12 +00:00
|
|
|
int args[MAX_VMMAIN_ARGS-1];
|
2011-02-18 14:31:32 +00:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, callnum);
|
2011-07-26 08:52:24 +00:00
|
|
|
for (i = 0; i < ARRAY_LEN(args); i++) {
|
2011-02-18 14:31:32 +00:00
|
|
|
args[i] = va_arg(ap, int);
|
|
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
r = vm->entryPoint( callnum, args[0], args[1], args[2], args[3],
|
|
|
|
args[4], args[5], args[6], args[7],
|
ioquake3 resync to revision 2398 from 2369.
This is the last ioquake3 revision before ioquake3 changed from subversion to git at the beginning of 2013.
#5808 - Include and use .glsl in source (rend2)
#5812 - Use refdef's coordinates when drawing to screen shadow fbo, and separate depth texture and screen texture coordinates in glsl shaders.
Include Rend2 renderer in MacOSX bundle
Include OpenGL1 and Rend2 renderers in MacOSX UB
Include Rend2 renderer in NSIS installer.
Include OpenGL1 and Rend2 renderers in Loki Setup Installer.
Have NSIS uninstaller delete rend2.
Split light sample into direct and ambient parts when using deluxemaps or per-vertex light vectors. Fixes #5813.
Fix writting voip data in demos (broke in r2102).
Fix server ignoring client move commands if voip data is included.
Allow changing cl_voip without restarting.
Fix assert failing in CL_ParseVoip() while flipping cl_voip off and on.
Only declare var_SampleToView in lightall shader when it is actually used.
Fix a couple files not ending with a newline.
Fix clients being able to reset their player state and respawn using donedl.
Fix passing arg9 (qvm only), arg10, and arg11 to vmMain for native libs and non-i386 compiled or interpated qvms. (Currently they aren't use in vmMain in game, cgame, or ui.)
Fix passing args[11] to args[15] from vm to engine on ppc64 and sparc64. Some of the args are used by game bot prediction syscalls. May have been causing bugs. Note: This was fixed for x86_64 in r2163.
Fix reconnect command to work after leaving server. (#5794)
Fix dedicated server crashing when using MSG_ReadDelta*, though it only happens if someone modifies the engine. (#5449)
Makefile fixes for OpenBSD by Jonathan Gray. (#5728)
Save all arguments from connect for reconnect command.
Remove unnecessary localhost check from reconnect command.
Support r_srgb even without hardware support. Also tweak default autoexposure/tonemap settings to look good on both r_srgb 0 and 1.
Changed the MacOS-X build system to make UB's containing i386 and x86_64 arches and made make-macosx.sh not build UB's but only standard binaries
Fix spectator client being switched from follow to free after map_restart if following a client with a higher client number.
Fix client unlinking issue caused by ent->s.number being set to followed client's ps->clientNum after map_restart. Reported by Ensiform.
Changes from Ensiform:
- In G_AddBot, try to allocate clientNum before doing anything else.
- In G_AddBot, don't set SVF_BOT and inuse. It's done in ClientConnect, plus inuse causes ClientDisconnect to be run for no reason.
- In G_AddBot, only set skill in bot useinfo once.
- Avoid using cl->ps.clientNum to check if cl is a bot.
Fix bot skill format so it doesn't always have a space at the beginning of it.
More fixes to the macosx buildsystem. This removes the SDL Framework and makes use of a SDL library that is position independant. This also brings back PPC builds into the UB and also as a standa alone build choice.
Have make-macosx.sh require the user to specify which architecture she/he wants to build for and suggest building UB's if the user is unaware of what architectures are
Lets list all the valid options.
2017-07-09 21:21:12 +00:00
|
|
|
args[8], args[9], args[10], args[11]);
|
2011-02-18 14:31:32 +00:00
|
|
|
} else {
|
2012-09-15 03:03:44 +00:00
|
|
|
#if ( id386 || idsparc ) && !defined __clang__ // calling convention doesn't need conversion in some cases
|
2011-02-18 14:31:32 +00:00
|
|
|
#ifndef NO_VM_COMPILED
|
|
|
|
if ( vm->compiled )
|
|
|
|
r = VM_CallCompiled( vm, (int*)&callnum );
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
r = VM_CallInterpreted( vm, (int*)&callnum );
|
|
|
|
#else
|
|
|
|
struct {
|
|
|
|
int callnum;
|
ioquake3 resync to revision 2398 from 2369.
This is the last ioquake3 revision before ioquake3 changed from subversion to git at the beginning of 2013.
#5808 - Include and use .glsl in source (rend2)
#5812 - Use refdef's coordinates when drawing to screen shadow fbo, and separate depth texture and screen texture coordinates in glsl shaders.
Include Rend2 renderer in MacOSX bundle
Include OpenGL1 and Rend2 renderers in MacOSX UB
Include Rend2 renderer in NSIS installer.
Include OpenGL1 and Rend2 renderers in Loki Setup Installer.
Have NSIS uninstaller delete rend2.
Split light sample into direct and ambient parts when using deluxemaps or per-vertex light vectors. Fixes #5813.
Fix writting voip data in demos (broke in r2102).
Fix server ignoring client move commands if voip data is included.
Allow changing cl_voip without restarting.
Fix assert failing in CL_ParseVoip() while flipping cl_voip off and on.
Only declare var_SampleToView in lightall shader when it is actually used.
Fix a couple files not ending with a newline.
Fix clients being able to reset their player state and respawn using donedl.
Fix passing arg9 (qvm only), arg10, and arg11 to vmMain for native libs and non-i386 compiled or interpated qvms. (Currently they aren't use in vmMain in game, cgame, or ui.)
Fix passing args[11] to args[15] from vm to engine on ppc64 and sparc64. Some of the args are used by game bot prediction syscalls. May have been causing bugs. Note: This was fixed for x86_64 in r2163.
Fix reconnect command to work after leaving server. (#5794)
Fix dedicated server crashing when using MSG_ReadDelta*, though it only happens if someone modifies the engine. (#5449)
Makefile fixes for OpenBSD by Jonathan Gray. (#5728)
Save all arguments from connect for reconnect command.
Remove unnecessary localhost check from reconnect command.
Support r_srgb even without hardware support. Also tweak default autoexposure/tonemap settings to look good on both r_srgb 0 and 1.
Changed the MacOS-X build system to make UB's containing i386 and x86_64 arches and made make-macosx.sh not build UB's but only standard binaries
Fix spectator client being switched from follow to free after map_restart if following a client with a higher client number.
Fix client unlinking issue caused by ent->s.number being set to followed client's ps->clientNum after map_restart. Reported by Ensiform.
Changes from Ensiform:
- In G_AddBot, try to allocate clientNum before doing anything else.
- In G_AddBot, don't set SVF_BOT and inuse. It's done in ClientConnect, plus inuse causes ClientDisconnect to be run for no reason.
- In G_AddBot, only set skill in bot useinfo once.
- Avoid using cl->ps.clientNum to check if cl is a bot.
Fix bot skill format so it doesn't always have a space at the beginning of it.
More fixes to the macosx buildsystem. This removes the SDL Framework and makes use of a SDL library that is position independant. This also brings back PPC builds into the UB and also as a standa alone build choice.
Have make-macosx.sh require the user to specify which architecture she/he wants to build for and suggest building UB's if the user is unaware of what architectures are
Lets list all the valid options.
2017-07-09 21:21:12 +00:00
|
|
|
int args[MAX_VMMAIN_ARGS-1];
|
2011-02-18 14:31:32 +00:00
|
|
|
} a;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
a.callnum = callnum;
|
|
|
|
va_start(ap, callnum);
|
2011-07-26 08:52:24 +00:00
|
|
|
for (i = 0; i < ARRAY_LEN(a.args); i++) {
|
2011-02-18 14:31:32 +00:00
|
|
|
a.args[i] = va_arg(ap, int);
|
|
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
#ifndef NO_VM_COMPILED
|
|
|
|
if ( vm->compiled )
|
|
|
|
r = VM_CallCompiled( vm, &a.callnum );
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
r = VM_CallInterpreted( vm, &a.callnum );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
--vm->callLevel;
|
|
|
|
|
|
|
|
if ( oldVM != NULL )
|
|
|
|
currentVM = oldVM;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
//=================================================================
|
|
|
|
|
|
|
|
static int QDECL VM_ProfileSort( const void *a, const void *b ) {
|
|
|
|
vmSymbol_t *sa, *sb;
|
|
|
|
|
|
|
|
sa = *(vmSymbol_t **)a;
|
|
|
|
sb = *(vmSymbol_t **)b;
|
|
|
|
|
|
|
|
if ( sa->profileCount < sb->profileCount ) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ( sa->profileCount > sb->profileCount ) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
VM_VmProfile_f
|
|
|
|
|
|
|
|
==============
|
|
|
|
*/
|
|
|
|
void VM_VmProfile_f( void ) {
|
|
|
|
vm_t *vm;
|
|
|
|
vmSymbol_t **sorted, *sym;
|
|
|
|
int i;
|
|
|
|
double total;
|
|
|
|
|
|
|
|
if ( !lastVM ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vm = lastVM;
|
|
|
|
|
|
|
|
if ( !vm->numSymbols ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sorted = Z_Malloc( vm->numSymbols * sizeof( *sorted ) );
|
|
|
|
sorted[0] = vm->symbols;
|
|
|
|
total = sorted[0]->profileCount;
|
|
|
|
for ( i = 1 ; i < vm->numSymbols ; i++ ) {
|
|
|
|
sorted[i] = sorted[i-1]->next;
|
|
|
|
total += sorted[i]->profileCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort( sorted, vm->numSymbols, sizeof( *sorted ), VM_ProfileSort );
|
|
|
|
|
|
|
|
for ( i = 0 ; i < vm->numSymbols ; i++ ) {
|
|
|
|
int perc;
|
|
|
|
|
|
|
|
sym = sorted[i];
|
|
|
|
|
|
|
|
perc = 100 * (float) sym->profileCount / total;
|
|
|
|
Com_Printf( "%2i%% %9i %s\n", perc, sym->profileCount, sym->symName );
|
|
|
|
sym->profileCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Com_Printf(" %9.0f total\n", total );
|
|
|
|
|
|
|
|
Z_Free( sorted );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
VM_VmInfo_f
|
|
|
|
|
|
|
|
==============
|
|
|
|
*/
|
|
|
|
void VM_VmInfo_f( void ) {
|
|
|
|
vm_t *vm;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
Com_Printf( "Registered virtual machines:\n" );
|
|
|
|
for ( i = 0 ; i < MAX_VM ; i++ ) {
|
|
|
|
vm = &vmTable[i];
|
|
|
|
if ( !vm->name[0] ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Com_Printf( "%s : ", vm->name );
|
|
|
|
if ( vm->dllHandle ) {
|
|
|
|
Com_Printf( "native\n" );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ( vm->compiled ) {
|
|
|
|
Com_Printf( "compiled on load\n" );
|
|
|
|
} else {
|
|
|
|
Com_Printf( "interpreted\n" );
|
|
|
|
}
|
|
|
|
Com_Printf( " code length : %7i\n", vm->codeLength );
|
|
|
|
Com_Printf( " table length: %7i\n", vm->instructionCount*4 );
|
|
|
|
Com_Printf( " data length : %7i\n", vm->dataMask + 1 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
===============
|
|
|
|
VM_LogSyscalls
|
|
|
|
|
|
|
|
Insert calls to this while debugging the vm compiler
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
void VM_LogSyscalls( int *args ) {
|
|
|
|
static int callnum;
|
|
|
|
static FILE *f;
|
|
|
|
|
|
|
|
if ( !f ) {
|
|
|
|
f = fopen("syscalls.log", "w" );
|
|
|
|
}
|
|
|
|
callnum++;
|
|
|
|
fprintf(f, "%i: %p (%i) = %i %i %i %i\n", callnum, (void*)(args - (int *)currentVM->dataBase),
|
|
|
|
args[0], args[1], args[2], args[3], args[4] );
|
|
|
|
}
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
VM_BlockCopy
|
|
|
|
Executes a block copy operation within currentVM data space
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void VM_BlockCopy(unsigned int dest, unsigned int src, size_t n)
|
|
|
|
{
|
|
|
|
unsigned int dataMask = currentVM->dataMask;
|
|
|
|
|
|
|
|
if ((dest & dataMask) != dest
|
|
|
|
|| (src & dataMask) != src
|
|
|
|
|| ((dest + n) & dataMask) != dest + n
|
|
|
|
|| ((src + n) & dataMask) != src + n)
|
|
|
|
{
|
|
|
|
Com_Error(ERR_DROP, "OP_BLOCK_COPY out of range!");
|
|
|
|
}
|
|
|
|
|
|
|
|
Com_Memcpy(currentVM->dataBase + dest, currentVM->dataBase + src, n);
|
|
|
|
}
|